fix: 优化物模型-标签定义
This commit is contained in:
parent
3231c7ea12
commit
c8fa001655
|
@ -10,6 +10,7 @@ export type DeviceInstance = {
|
|||
protocolName: string;
|
||||
security: any;
|
||||
deriveMetadata: string;
|
||||
productMetadata: string;
|
||||
metadata: string;
|
||||
binds: any;
|
||||
state: {
|
||||
|
|
|
@ -331,8 +331,9 @@ const submitData = () => {
|
|||
form.classifiedName
|
||||
? form.classifiedName
|
||||
: (form.classifiedName = '');
|
||||
const res = await editProduct(form);
|
||||
loading.value = false
|
||||
const res = await editProduct(form).finally(() => {
|
||||
loading.value = false
|
||||
});
|
||||
if (res.status === 200) {
|
||||
message.success('保存成功!');
|
||||
emit('success');
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
<template #expand>
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
v-if="!dataSource.length"
|
||||
v-if="!showSave"
|
||||
:hasPermission="`${permission}:update`"
|
||||
key="add"
|
||||
@click="handleAddClick()"
|
||||
|
||||
:disabled="hasOperate('add', type)"
|
||||
:tooltip="{
|
||||
placement: hasOperate('add', type) ? 'topRight' : 'top',
|
||||
|
@ -23,6 +23,7 @@
|
|||
? '当前的存储方式不支持新增'
|
||||
: '新增',
|
||||
}"
|
||||
@click="handleAddClick()"
|
||||
>
|
||||
新增
|
||||
</PermissionButton>
|
||||
|
@ -31,7 +32,8 @@
|
|||
:hasPermission="`${permission}:update`"
|
||||
key="update"
|
||||
v-else
|
||||
@click="handleSaveClick"
|
||||
:loading="loading"
|
||||
|
||||
:disabled="hasOperate('add', type)"
|
||||
:tooltip="{
|
||||
title: hasOperate('add', type)
|
||||
|
@ -39,6 +41,7 @@
|
|||
: '保存',
|
||||
placement: hasOperate('add', type) ? 'topRight' : 'top',
|
||||
}"
|
||||
@click="handleSaveClick"
|
||||
>
|
||||
保存
|
||||
</PermissionButton>
|
||||
|
@ -89,7 +92,7 @@
|
|||
{{ sourceMap?.[data.record?.expands?.source] || '' }}
|
||||
</template>
|
||||
<template #inputs="{ data }">
|
||||
<InputParams v-model:value="data.record" />
|
||||
<InputParams v-model:value="data.record.inputs" />
|
||||
</template>
|
||||
<template #output="{ data }">
|
||||
{{ data.record.output?.type }}
|
||||
|
@ -101,7 +104,7 @@
|
|||
{{ levelMap?.[data.record.expands?.level] || '-' }}
|
||||
</template>
|
||||
<template #properties="{ data }">
|
||||
<ConfigParams v-model:value="data.record" />
|
||||
<ConfigParams v-model:value="data.record.valueType" />
|
||||
</template>
|
||||
<template #outInput>
|
||||
object
|
||||
|
@ -112,7 +115,7 @@
|
|||
</j-tag>
|
||||
</template>
|
||||
<template #other="{ data }">
|
||||
<OtherSetting v-model:value="data.record" />
|
||||
<OtherSetting v-model:value="data.record.expands" :type="data.record.valueType.type" />
|
||||
</template>
|
||||
<template #action="{data}">
|
||||
<j-space>
|
||||
|
@ -143,7 +146,6 @@
|
|||
<AIcon type="PlusSquareOutlined" />
|
||||
</PermissionButton>
|
||||
<PermissionButton
|
||||
v-if="type !== 'tags'"
|
||||
:has-permission="true"
|
||||
type="link"
|
||||
key="edit"
|
||||
|
@ -163,9 +165,9 @@
|
|||
danger
|
||||
:pop-confirm="{
|
||||
placement: 'topRight',
|
||||
title: dataSource.length === 1 ? '这是最后一条数据了,确认删除?' : '确认删除?',
|
||||
title: showSave ? '这是最后一条数据了,确认删除?' : '确认删除?',
|
||||
onConfirm: async () => {
|
||||
await removeItem(data.index, dataSource.length === 1);
|
||||
await removeItem(data.index, showSave);
|
||||
},
|
||||
}"
|
||||
:tooltip="{
|
||||
|
@ -192,6 +194,11 @@
|
|||
:data="detailData.data"
|
||||
@cancel="cancelDetailModal"
|
||||
/>
|
||||
<TagsModal
|
||||
v-else-if="type === 'tags' && detailData.visible"
|
||||
:data="detailData.data"
|
||||
@cancel="cancelDetailModal"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="BaseMetadata">
|
||||
|
@ -216,13 +223,13 @@ import { DeviceInstance } from '@/views/device/Instance/typings';
|
|||
import { onlyMessage } from '@/utils/comm';
|
||||
import {omit} from "lodash-es";
|
||||
import {useAction} from "@/views/device/components/Metadata/Base/hooks/useAction";
|
||||
import { PropertiesModal, FunctionModal, EventModal } from './DetailModal'
|
||||
import { PropertiesModal, FunctionModal, EventModal, TagsModal } from './DetailModal'
|
||||
|
||||
const props = defineProps({
|
||||
// target: {
|
||||
// type: String as PropType<'device' | 'product'>,
|
||||
// default: 'product',
|
||||
// },
|
||||
target: {
|
||||
type: String as PropType<'device' | 'product'>,
|
||||
default: 'product',
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<MetadataType>,
|
||||
default: undefined,
|
||||
|
@ -233,10 +240,10 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
const target = inject<'device' | 'product'>('_metadataType', 'product');
|
||||
const _target = inject<'device' | 'product'>('_metadataType', props.target);
|
||||
|
||||
const { data: metadata, noEdit } = useMetadata(target, props.type);
|
||||
const { hasOperate } = useOperateLimits(target);
|
||||
const { data: metadata, noEdit } = useMetadata(_target, props.type);
|
||||
const { hasOperate } = useOperateLimits(_target);
|
||||
|
||||
const metadataStore = useMetadataStore()
|
||||
const instanceStore = useInstanceStore()
|
||||
|
@ -244,14 +251,18 @@ const productStore = useProductStore()
|
|||
|
||||
const dataSource = ref<MetadataItem[]>(metadata.value || []);
|
||||
const tableRef = ref();
|
||||
const loading = ref(false)
|
||||
|
||||
// const columns = computed(() => MetadataMapping.get(props.type!));
|
||||
const {columns} = useColumns(props.type, target, noEdit)
|
||||
const {columns} = useColumns(props.type, _target, noEdit)
|
||||
|
||||
const detailData = reactive({
|
||||
data: {},
|
||||
visible:false
|
||||
})
|
||||
|
||||
const showSave = ref(!!metadata.value.length)
|
||||
|
||||
const { addAction, copyAction, removeAction } = useAction(tableRef)
|
||||
|
||||
provide('_dataSource', dataSource.value)
|
||||
|
@ -268,7 +279,7 @@ const cancelDetailModal = () => {
|
|||
|
||||
const operateLimits = (action: 'add' | 'updata', types: MetadataType) => {
|
||||
return (
|
||||
target === 'device' &&
|
||||
_target === 'device' &&
|
||||
(instanceStore.detail.features || []).find((item: { id: string; name: string }) => item.id === limitsMap.get(`${types}-${action}`))
|
||||
);
|
||||
};
|
||||
|
@ -339,21 +350,23 @@ const handleAddClick = async (_data?: any, index?: number) => {
|
|||
|
||||
|
||||
|
||||
const data = [...dataSource.value];
|
||||
|
||||
if (index !== undefined) {
|
||||
// 校验
|
||||
const _data = await tableRef.value.getData()
|
||||
console.log(_data)
|
||||
if (_data) {
|
||||
data.splice(index + 1, 0, newObject);
|
||||
}
|
||||
} else {
|
||||
data.push(newObject);
|
||||
}
|
||||
dataSource.value = data
|
||||
const _index = index !== undefined ? index + 1 : 0
|
||||
tableRef.value?.addItemAll?.(_index)
|
||||
// const data = [...dataSource.value];
|
||||
//
|
||||
// if (index !== undefined) {
|
||||
// // 校验
|
||||
// const _data = await tableRef.value.getData()
|
||||
// console.log(_data)
|
||||
// if (_data) {
|
||||
// data.splice(index + 1, 0, newObject);
|
||||
// }
|
||||
// } else {
|
||||
// data.push(newObject);
|
||||
// }
|
||||
// dataSource.value = data
|
||||
tableRef.value.addItem(newObject, index)
|
||||
showSave.value = true
|
||||
// const _index = index !== undefined ? index + 1 : 0
|
||||
// tableRef.value?.addItemAll?.(_index)
|
||||
};
|
||||
|
||||
const copyItem = (record: any, index: number) => {
|
||||
|
@ -363,9 +376,13 @@ const copyItem = (record: any, index: number) => {
|
|||
}
|
||||
|
||||
const removeItem = (index: number, isSave: false) => {
|
||||
const data = [...dataSource.value];
|
||||
data.splice(index, 1);
|
||||
dataSource.value = data
|
||||
// const data = [...dataSource.value];
|
||||
// data.splice(index, 1);
|
||||
// dataSource.value = data
|
||||
const _data = tableRef.value.removeItem(index)
|
||||
if (_data.length === 0) {
|
||||
showSave.value = false
|
||||
}
|
||||
if (isSave) {
|
||||
handleSaveClick()
|
||||
}
|
||||
|
@ -399,7 +416,7 @@ const handleSaveClick = async () => {
|
|||
// 保存规则
|
||||
if(virtual.length) {
|
||||
let res = undefined
|
||||
if(target === 'device') {
|
||||
if(_target === 'device') {
|
||||
res = await saveDeviceVirtualProperty(instanceStore.current.productId, instanceStore.current.id, virtual)
|
||||
} else {
|
||||
res = await saveProductVirtualProperty(productStore.current.id, virtual)
|
||||
|
@ -407,7 +424,7 @@ const handleSaveClick = async () => {
|
|||
}
|
||||
// 保存属性
|
||||
const updateStore = (metadata: string) => {
|
||||
if (target === 'device') {
|
||||
if (_target === 'device') {
|
||||
const detail = instanceStore.current
|
||||
detail.metadata = metadata
|
||||
instanceStore.setCurrent(detail)
|
||||
|
@ -417,10 +434,12 @@ const handleSaveClick = async () => {
|
|||
productStore.setCurrent(detail)
|
||||
}
|
||||
}
|
||||
const _detail: ProductItem | DeviceInstance = target === 'device' ? instanceStore.detail : productStore.current
|
||||
const _detail: ProductItem | DeviceInstance = _target === 'device' ? instanceStore.detail : productStore.current
|
||||
let _data = updateMetadata(props.type!, arr, _detail, updateStore)
|
||||
|
||||
const result = await asyncUpdateMetadata(target, _data)
|
||||
loading.value = true
|
||||
const result = await asyncUpdateMetadata(_target, _data).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
if(result.success) {
|
||||
dataSource.value = resp
|
||||
onlyMessage('操作成功!')
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<j-modal
|
||||
visible
|
||||
:maskClosable="false"
|
||||
title="事件详情"
|
||||
width="650px"
|
||||
@cancel="cancel"
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
visible
|
||||
title="功能详情"
|
||||
width="650px"
|
||||
:maskClosable="false"
|
||||
@cancel="cancel"
|
||||
@ok="ok"
|
||||
>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<j-modal
|
||||
visible
|
||||
:maskClosable="false"
|
||||
title="属性详情"
|
||||
@cancel="cancel"
|
||||
@ok="ok"
|
||||
|
@ -15,7 +16,7 @@
|
|||
>
|
||||
<a-descriptions-item label="属性标识">{{ data.id }}</a-descriptions-item>
|
||||
<a-descriptions-item label="属性名称">{{ data.name }}</a-descriptions-item>
|
||||
<a-descriptions-item label="属性类型">{{ data.valueType.type }}</a-descriptions-item>
|
||||
<a-descriptions-item label="属性类型">{{ TypeStringMap[data.valueType.type] }}</a-descriptions-item>
|
||||
<a-descriptions-item v-if="['int', 'long', 'float', 'double'].includes(data.valueType.type)" label="单位">{{ unitLabel }}</a-descriptions-item>
|
||||
<a-descriptions-item v-if="['float', 'double'].includes(data.valueType.type)" label="精度">{{ data.valueType?.scale }}</a-descriptions-item>
|
||||
<a-descriptions-item v-if="['string', 'password'].includes(data.valueType.type)" label="最大长度">{{ data.valueType?.maxLength }}</a-descriptions-item>
|
||||
|
@ -40,7 +41,11 @@
|
|||
:pagination="false"
|
||||
size="small"
|
||||
>
|
||||
|
||||
<template #bodyCell="{column, record}">
|
||||
<span v-if="column.dataIndex === 'value'">
|
||||
{{ record.range === 'true' ? record.value?.join('-') : record.value }}
|
||||
</span>
|
||||
</template>
|
||||
</j-table>
|
||||
</a-descriptions-item>
|
||||
</j-descriptions>
|
||||
|
@ -56,6 +61,7 @@ import {omit} from "lodash-es";
|
|||
import {watch} from "vue";
|
||||
import JsonView from './JsonView.vue'
|
||||
import {getUnit} from "@/api/device/instance";
|
||||
import {TypeStringMap} from "@/views/device/components/Metadata/Base/columns";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
|
@ -93,7 +99,7 @@ const metrics = reactive<{ columns: any[], dataSource: any }>({
|
|||
columns: [
|
||||
{ title: '指标标识', dataIndex: 'id' },
|
||||
{ title: '指标名称', dataIndex: 'name' },
|
||||
{ title: '指标治', dataIndex: 'value' },
|
||||
{ title: '指标值', dataIndex: 'value' },
|
||||
],
|
||||
dataSource: []
|
||||
})
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
<template>
|
||||
<j-modal
|
||||
visible
|
||||
:maskClosable="false"
|
||||
title="标签详情"
|
||||
@cancel="cancel"
|
||||
@ok="ok"
|
||||
>
|
||||
<j-descriptions
|
||||
:column="1"
|
||||
:labelStyle="{
|
||||
width: '72px',
|
||||
textAlign: 'right',
|
||||
justifyContent: 'end'
|
||||
}"
|
||||
>
|
||||
<a-descriptions-item label="标签标识">{{ data.id }}</a-descriptions-item>
|
||||
<a-descriptions-item label="标签名称">{{ data.name }}</a-descriptions-item>
|
||||
<a-descriptions-item label="标签类型">{{ data.valueType.type }}</a-descriptions-item>
|
||||
<a-descriptions-item v-if="['int', 'long', 'float', 'double'].includes(data.valueType.type)" label="单位">{{ unitLabel }}</a-descriptions-item>
|
||||
<a-descriptions-item v-if="['float', 'double'].includes(data.valueType.type)" label="精度">{{ data.valueType?.scale }}</a-descriptions-item>
|
||||
<a-descriptions-item v-if="['string', 'password'].includes(data.valueType.type)" label="最大长度">{{ data.valueType?.maxLength }}</a-descriptions-item>
|
||||
<a-descriptions-item v-if="data.valueType.type === 'file'" label="文件类型">{{ data.valueType?.fileType }}</a-descriptions-item>
|
||||
<a-descriptions-item v-if="data.valueType.type === 'date'" label="格式">{{ data.valueType?.format }}</a-descriptions-item>
|
||||
<a-descriptions-item
|
||||
v-if="
|
||||
['enum', 'object', 'boolean', 'array'].includes(data.valueType.type)"
|
||||
>
|
||||
<JsonView :value="dataTypeTable.dataSource"/>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="读写类型">{{ readTypeText }}</a-descriptions-item>
|
||||
</j-descriptions>
|
||||
<template #footer>
|
||||
<j-button type="primary" @click="ok">确认</j-button>
|
||||
</template>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="TagsModal">
|
||||
import {OtherConfigInfo} from "@/views/device/components/Metadata/Base/components";
|
||||
import {omit} from "lodash-es";
|
||||
import {watch} from "vue";
|
||||
import JsonView from './JsonView.vue'
|
||||
import {getUnit} from "@/api/device/instance";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['cancel'])
|
||||
|
||||
const sourceMap = {
|
||||
'device': '设备',
|
||||
'manual': '手动',
|
||||
'rule': '规则',
|
||||
}
|
||||
|
||||
const readTypeText = computed(() => {
|
||||
const type = {
|
||||
"read": "读",
|
||||
"write": "写",
|
||||
"report": "上报",
|
||||
}
|
||||
|
||||
return props.data?.expands?.type?.map?.((key: string) => type[key]).join('、')
|
||||
})
|
||||
|
||||
const unitLabel = ref('')
|
||||
|
||||
const dataTypeTable = reactive<{ columns: any[], dataSource: any }>({
|
||||
columns: [],
|
||||
dataSource: []
|
||||
})
|
||||
|
||||
const metrics = reactive<{ columns: any[], dataSource: any }>({
|
||||
columns: [
|
||||
{ title: '指标标识', dataIndex: 'id' },
|
||||
{ title: '指标名称', dataIndex: 'name' },
|
||||
{ title: '指标治', dataIndex: 'value' },
|
||||
],
|
||||
dataSource: []
|
||||
})
|
||||
|
||||
|
||||
const showSetting = computed(() => {
|
||||
const setting = omit((props.data?.expands || {}), ['type', 'source'])
|
||||
return Object.values(setting).length
|
||||
})
|
||||
|
||||
const handleDataTable = (type: string) => {
|
||||
switch (type) {
|
||||
case 'enum':
|
||||
dataTypeTable.columns = [
|
||||
{ title: 'Value', dataIndex: 'value'},
|
||||
{ title: 'Text', dataIndex: 'text'},
|
||||
]
|
||||
dataTypeTable.dataSource = props.data.valueType?.elements
|
||||
break;
|
||||
case 'object':
|
||||
dataTypeTable.columns = [
|
||||
{ title: '标识', dataIndex: 'id', width: 50},
|
||||
{ title: '名称', dataIndex: 'name'},
|
||||
{ title: '名称', dataIndex: 'name'},
|
||||
]
|
||||
dataTypeTable.dataSource = props.data.valueType?.properties
|
||||
break;
|
||||
case 'boolean':
|
||||
dataTypeTable.dataSource = {
|
||||
...omit(props.data.valueType, ['type'])
|
||||
}
|
||||
break;
|
||||
case 'array':
|
||||
dataTypeTable.dataSource = props.data.valueType.elementType
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
watch(() => props.data.valueType.type, () => {
|
||||
const type = props.data.valueType.type
|
||||
handleDataTable(props.data.valueType.type)
|
||||
|
||||
if (['float', 'double', 'int', 'long'].includes(type)) {
|
||||
getUnit().then((res) => {
|
||||
if (res.success) {
|
||||
res.result.map((item) => {
|
||||
if (item.id === props.data.valueType?.unit) {
|
||||
unitLabel.value = item.description
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
const cancel = () => {
|
||||
emit('cancel')
|
||||
}
|
||||
|
||||
const ok = () => {
|
||||
cancel()
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,3 +1,4 @@
|
|||
export { default as PropertiesModal } from './PropertiesModal.vue'
|
||||
export { default as FunctionModal } from './FunctionModal.vue'
|
||||
export { default as EventModal } from './EventModal.vue'
|
||||
export { default as EventModal } from './EventModal.vue'
|
||||
export { default as TagsModal } from './TagsModal.vue'
|
|
@ -6,6 +6,7 @@ import { EventLevel } from "@/views/device/data";
|
|||
import {MetadataType} from "@/views/device/Product/typings";
|
||||
import { getUnit } from '@/api/device/instance';
|
||||
import {Ref} from "vue";
|
||||
import {omit} from "lodash-es";
|
||||
interface DataTableColumnProps extends ColumnProps {
|
||||
type?: string,
|
||||
components?: {
|
||||
|
@ -39,7 +40,9 @@ export const handleTypeValue = (type:string, value: any = {}) => {
|
|||
obj.elementType = value
|
||||
break;
|
||||
case 'object':
|
||||
obj.properties = value
|
||||
obj.properties = (value || []).map((item: any) => {
|
||||
return omit(item, ['config', 'action', '_sortIndex'])
|
||||
})
|
||||
break;
|
||||
case 'int':
|
||||
case 'long':
|
||||
|
@ -119,13 +122,19 @@ export const useColumns = (type?: MetadataType, target?: 'device' | 'product', n
|
|||
const fieldIndex = Number(field[1])
|
||||
const hasId = dataSource.some((item, index) => item.id === value && fieldIndex !== index)
|
||||
if (hasId) {
|
||||
return Promise.reject('该标识存在')
|
||||
return Promise.reject('该标识已存在')
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
return Promise.reject('请输入标识')
|
||||
}
|
||||
}]
|
||||
},
|
||||
},
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
{
|
||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
||||
},
|
||||
]
|
||||
},
|
||||
doubleClick(record) {
|
||||
const ids = (noEdit?.value?.id || []) as any[]
|
||||
|
@ -139,10 +148,13 @@ export const useColumns = (type?: MetadataType, target?: 'device' | 'product', n
|
|||
type: 'text',
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入名称'
|
||||
}]
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入名称'
|
||||
},
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
]
|
||||
}
|
||||
},
|
||||
];
|
||||
|
@ -262,9 +274,10 @@ export const useColumns = (type?: MetadataType, target?: 'device' | 'product', n
|
|||
},
|
||||
form: {
|
||||
required: true,
|
||||
rules: [
|
||||
rules: target !== 'device' ? [
|
||||
{
|
||||
validator: async (_: Record<string, any>, value: any) => {
|
||||
console.log('expands',value)
|
||||
if (value.source) {
|
||||
if(value.source !== 'rule') {
|
||||
if(value.type?.length) {
|
||||
|
@ -284,7 +297,7 @@ export const useColumns = (type?: MetadataType, target?: 'device' | 'product', n
|
|||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
]: []
|
||||
},
|
||||
width: 150
|
||||
},
|
||||
|
@ -307,7 +320,19 @@ export const useColumns = (type?: MetadataType, target?: 'device' | 'product', n
|
|||
type: 'components',
|
||||
components: {
|
||||
name: DataType,
|
||||
}
|
||||
},
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
validator(_: any, value: any) {
|
||||
console.log('validator',value)
|
||||
if (!value?.type) {
|
||||
return Promise.reject('请选择数据类型')
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
}]
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '读写类型',
|
||||
|
@ -332,7 +357,6 @@ export const useColumns = (type?: MetadataType, target?: 'device' | 'product', n
|
|||
const columns = ref<any[]>([])
|
||||
|
||||
watch(() => JSON.stringify(noEdit!.value), () => {
|
||||
console.log(noEdit!.value)
|
||||
switch(type) {
|
||||
case 'properties':
|
||||
columns.value = PropertyColumns
|
||||
|
@ -356,22 +380,40 @@ export const useUnit = (type: Ref<string>) => {
|
|||
const unitOptions = ref<Array<{ label: string, value: any }>>([])
|
||||
|
||||
watch(() => type.value, () => {
|
||||
console.log('type.value',type.value)
|
||||
if (['float', 'double', 'int', 'long'].includes(type.value) && !unitOptions.value.length) {
|
||||
getUnit().then((res) => {
|
||||
if (res.success) {
|
||||
unitOptions.value = res.result.map((item: any) => ({
|
||||
label: item.description,
|
||||
value: item.id,
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
|
||||
return { unitOptions }
|
||||
}
|
||||
|
||||
|
||||
export const TypeStringMap = {
|
||||
int: 'int(整数型)',
|
||||
long: 'long(长整数型)',
|
||||
float: 'float(单精度浮点型)',
|
||||
double: 'double(双精度浮点数)',
|
||||
string: 'text(字符串)',
|
||||
boolean: 'boolean(布尔型)',
|
||||
date: 'date(时间型)',
|
||||
enum: 'enum(枚举)',
|
||||
array: 'array(数组)',
|
||||
object: 'object(结构体)',
|
||||
file: 'file(文件)',
|
||||
password: 'password(密码)',
|
||||
geoPoint: 'geoPoint(地理位置)',
|
||||
}
|
||||
|
||||
// const MetadataMapping = new Map<string, DataTableColumnProps[]>();
|
||||
// MetadataMapping.set('properties', PropertyColumns);
|
||||
// MetadataMapping.set('events', EventColumns);
|
||||
|
|
|
@ -6,94 +6,23 @@
|
|||
placement="topRight"
|
||||
@confirm="valueChange"
|
||||
>
|
||||
<j-button>
|
||||
<AIcon type="SettingOutlined" />
|
||||
配置
|
||||
</j-button>
|
||||
<ModelButton />
|
||||
</DataTableArray>
|
||||
<DataTableObject
|
||||
v-else-if="type === 'object'"
|
||||
v-else-if="type === 'object' && showOther"
|
||||
:value="myValue.properties"
|
||||
placement="topRight"
|
||||
:onAdd="objectAdd"
|
||||
:columns="[
|
||||
{
|
||||
title: '参数标识',
|
||||
dataIndex: 'id',
|
||||
type: 'text',
|
||||
width: 100,
|
||||
form: {
|
||||
required: true,
|
||||
rules: [
|
||||
{
|
||||
callback(rule:any,value: any, _dataSource: any[]) {
|
||||
if (value) {
|
||||
const field = rule.field.split('.')
|
||||
const fieldIndex = Number(field[1])
|
||||
const hasId = _dataSource.some((item, index) => item.id === value && fieldIndex !== index)
|
||||
if (hasId) {
|
||||
return Promise.reject('该标识存在')
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
return Promise.reject('请输入标识')
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '参数名称',
|
||||
dataIndex: 'name',
|
||||
type: 'text',
|
||||
width: 100,
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入参数名称'
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '数据类型',
|
||||
type: 'components',
|
||||
dataIndex: 'valueType',
|
||||
components: {
|
||||
name: ValueObject,
|
||||
props: {
|
||||
filter: ['object']
|
||||
}
|
||||
},
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '其他配置',
|
||||
type: 'components',
|
||||
dataIndex: 'config',
|
||||
components: {
|
||||
name: DataTypeObjectChild
|
||||
},
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
width: 60
|
||||
}
|
||||
]"
|
||||
:columns="columns"
|
||||
@confirm="valueChange"
|
||||
>
|
||||
<template #valueType="{ data }">
|
||||
{{ data.record.valueType?.type }}
|
||||
{{ TypeStringMap[data.record.valueType?.type] }}
|
||||
</template>
|
||||
<template #config="{ data }">
|
||||
<OtherConfigInfo :value="data.record.valueType"></OtherConfigInfo>
|
||||
</template>
|
||||
<j-button>
|
||||
<AIcon type="SettingOutlined" />
|
||||
配置
|
||||
</j-button>
|
||||
<ModelButton />
|
||||
</DataTableObject>
|
||||
<DataTableEnum
|
||||
v-else-if="type === 'enum'"
|
||||
|
@ -101,10 +30,7 @@
|
|||
placement="topRight"
|
||||
@confirm="valueChange"
|
||||
>
|
||||
<j-button>
|
||||
<AIcon type="SettingOutlined" />
|
||||
配置
|
||||
</j-button>
|
||||
<ModelButton />
|
||||
</DataTableEnum>
|
||||
<DataTableBoolean
|
||||
v-else-if="type === 'boolean'"
|
||||
|
@ -112,34 +38,26 @@
|
|||
placement="topRight"
|
||||
@confirm="valueChange"
|
||||
>
|
||||
<j-button>
|
||||
<AIcon type="SettingOutlined" />
|
||||
配置
|
||||
</j-button>
|
||||
<ModelButton />
|
||||
</DataTableBoolean>
|
||||
<DataTableDouble
|
||||
v-else-if="['float', 'double'].includes(type)"
|
||||
:options="unitOptions"
|
||||
:showUnit="false"
|
||||
v-model:value="myValue"
|
||||
placement="topRight"
|
||||
@confirm="valueChange"
|
||||
>
|
||||
<j-button>
|
||||
<AIcon type="SettingOutlined" />
|
||||
配置
|
||||
</j-button>
|
||||
<ModelButton />
|
||||
</DataTableDouble>
|
||||
<DataTableInteger
|
||||
v-else-if="['int', 'long'].includes(type)"
|
||||
v-else-if="['int', 'long'].includes(type) && showOther"
|
||||
:options="unitOptions"
|
||||
v-model:value="myValue.unit"
|
||||
placement="topRight"
|
||||
@confirm="valueChange"
|
||||
>
|
||||
<j-button>
|
||||
<AIcon type="SettingOutlined" />
|
||||
配置
|
||||
</j-button>
|
||||
<ModelButton />
|
||||
</DataTableInteger>
|
||||
<DataTableFile
|
||||
v-else-if="type === 'file'"
|
||||
|
@ -147,10 +65,7 @@
|
|||
placement="topRight"
|
||||
@confirm="valueChange"
|
||||
>
|
||||
<j-button>
|
||||
<AIcon type="SettingOutlined" />
|
||||
配置
|
||||
</j-button>
|
||||
<ModelButton />
|
||||
</DataTableFile>
|
||||
<DataTableDate
|
||||
v-else-if="type === 'date'"
|
||||
|
@ -158,20 +73,14 @@
|
|||
placement="topRight"
|
||||
@confirm="valueChange"
|
||||
>
|
||||
<j-button>
|
||||
<AIcon type="SettingOutlined" />
|
||||
配置
|
||||
</j-button>
|
||||
<ModelButton />
|
||||
</DataTableDate>
|
||||
<DataTableString
|
||||
v-else-if="['string', 'password'].includes(type)"
|
||||
v-model:value="myValue.maxLength"
|
||||
placement="topRight"
|
||||
>
|
||||
<j-button>
|
||||
<AIcon type="SettingOutlined" />
|
||||
配置
|
||||
</j-button>
|
||||
<ModelButton />
|
||||
</DataTableString>
|
||||
<span v-else>
|
||||
无
|
||||
|
@ -203,11 +112,16 @@ const props = defineProps({
|
|||
valueKey: {
|
||||
type: String,
|
||||
default: 'valueType'
|
||||
},
|
||||
showOther: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:value'])
|
||||
import {useUnit} from "@/views/device/components/Metadata/Base/columns";
|
||||
import {handleTypeValue, TypeStringMap, useUnit} from "@/views/device/components/Metadata/Base/columns";
|
||||
import ModelButton from '@/views/device/components/Metadata/Base/components/ModelButton.vue'
|
||||
|
||||
const objectAdd = () => {
|
||||
return {
|
||||
|
@ -221,20 +135,113 @@ const objectAdd = () => {
|
|||
|
||||
const options = ref([])
|
||||
|
||||
const type = ref(props.value?.[props.valueKey]?.type)
|
||||
const type = ref(props.value?.type)
|
||||
|
||||
const { unitOptions } = useUnit(type)
|
||||
|
||||
const myValue = ref(props.value?.[props.valueKey])
|
||||
const myValue = ref(props.value)
|
||||
|
||||
const valueChange = () => {
|
||||
const columns = [
|
||||
{
|
||||
title: '参数标识',
|
||||
dataIndex: 'id',
|
||||
type: 'text',
|
||||
width: 100,
|
||||
form: {
|
||||
required: true,
|
||||
rules: [
|
||||
{
|
||||
callback(rule:any,value: any, _dataSource: any[]) {
|
||||
if (value) {
|
||||
const field = rule.field.split('.')
|
||||
const fieldIndex = Number(field[1])
|
||||
const hasId = _dataSource.some((item, index) => item.id === value && fieldIndex !== index)
|
||||
if (hasId) {
|
||||
return Promise.reject('该标识已存在')
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
return Promise.reject('请输入标识')
|
||||
}
|
||||
},
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
{
|
||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '参数名称',
|
||||
dataIndex: 'name',
|
||||
type: 'text',
|
||||
width: 100,
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入参数名称'
|
||||
},
|
||||
{ max: 64, message: '最多可输入64个字符' }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '数据类型',
|
||||
type: 'components',
|
||||
dataIndex: 'valueType',
|
||||
components: {
|
||||
name: ValueObject,
|
||||
},
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
validator(_: any, value: any) {
|
||||
console.log('validator',value)
|
||||
if (!value?.type) {
|
||||
return Promise.reject('请选择数据类型')
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
}]
|
||||
},
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '其他配置',
|
||||
type: 'components',
|
||||
dataIndex: 'config',
|
||||
components: {
|
||||
name: DataTypeObjectChild
|
||||
},
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
width: 60
|
||||
}
|
||||
]
|
||||
|
||||
const valueChange = (data: any) => {
|
||||
console.log('configModal - confirm',data, props.value, type.value)
|
||||
const newObj = handleTypeValue(type.value, data)
|
||||
console.log('configModal - newObj', newObj)
|
||||
console.log('configModal - newObj2', {
|
||||
type: type.value,
|
||||
...newObj
|
||||
})
|
||||
emit('update:value', {
|
||||
type: type.value,
|
||||
...newObj
|
||||
})
|
||||
}
|
||||
|
||||
watch(() => JSON.stringify(props.value), () => {
|
||||
console.log(props.value)
|
||||
type.value = props.value?.[props.valueKey]?.type
|
||||
myValue.value = props.value?.[props.valueKey]
|
||||
type.value = props.value?.type
|
||||
myValue.value = props.value
|
||||
})
|
||||
|
||||
</script>
|
||||
|
|
|
@ -18,25 +18,25 @@
|
|||
@confirm="(data) => {valueChange(data, 'object')}"
|
||||
>
|
||||
<template #valueType="{ data }">
|
||||
{{ data.record.valueType?.type }}
|
||||
{{ TypeStringMap[data.record.valueType?.type] }}
|
||||
</template>
|
||||
<template #config="{ data }">
|
||||
<!-- <OtherConfigInfo :value="data.record.valueType"></OtherConfigInfo>-->
|
||||
<ConfigModal v-model:value="data.record" :unitOptions="unitOptions" />
|
||||
<ConfigModal v-model:value="data.record.valueType" :showOther="false" />
|
||||
</template>
|
||||
</DataTableObject>
|
||||
<DataTableEnum v-else-if="type === 'enum'" v-model:value="_valueType" placement="topRight" @confirm="(data) => {valueChange(data, 'enum')}"/>
|
||||
<DataTableBoolean v-else-if="type === 'boolean'" v-model:value="_valueType" placement="topRight" @confirm="(data) => {valueChange(data, 'boolean')}" />
|
||||
<DataTableDouble
|
||||
v-else-if="['float', 'double'].includes(type)"
|
||||
:options="options"
|
||||
:options="unitOptions"
|
||||
v-model:value="_valueType"
|
||||
placement="topRight"
|
||||
@confirm="(data) => {valueChange(data, 'float')}"
|
||||
/>
|
||||
<DataTableInteger
|
||||
v-else-if="['int', 'long'].includes(type)"
|
||||
:options="options"
|
||||
:options="unitOptions"
|
||||
v-model:value="_valueType.unit"
|
||||
placement="topRight"
|
||||
@confirm="(data) => {valueChange(data, 'int')}"
|
||||
|
@ -68,8 +68,9 @@ import {
|
|||
DataTableObject,
|
||||
} from 'jetlinks-ui-components';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import {handleTypeValue, typeSelectChange, useUnit } from '../columns'
|
||||
import {handleTypeValue, typeSelectChange, TypeStringMap, useUnit} from '../columns'
|
||||
import ConfigModal from './ConfigModal.vue'
|
||||
import { Form } from 'jetlinks-ui-components'
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
|
@ -78,6 +79,8 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
const formItemContext = Form.useInjectFormItemContext();
|
||||
|
||||
const columns = [{
|
||||
title: '参数标识',
|
||||
dataIndex: 'id',
|
||||
|
@ -86,10 +89,25 @@ const columns = [{
|
|||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
callback() {
|
||||
return Promise.resolve()
|
||||
}
|
||||
}]
|
||||
callback(rule:any,value: any, dataSource: any[]) {
|
||||
if (value) {
|
||||
const field = rule.field.split('.')
|
||||
const fieldIndex = Number(field[1])
|
||||
const hasId = dataSource.some((item, index) => item.id === value && fieldIndex !== index)
|
||||
if (hasId) {
|
||||
return Promise.reject('该标识已存在')
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
return Promise.reject('请输入标识')
|
||||
},
|
||||
},
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
{
|
||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -102,7 +120,9 @@ const columns = [{
|
|||
rules: [{
|
||||
required: true,
|
||||
message: '请输入参数名称'
|
||||
}]
|
||||
},
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -111,9 +131,18 @@ const columns = [{
|
|||
dataIndex: 'valueType',
|
||||
components: {
|
||||
name: ValueObject,
|
||||
props: {
|
||||
filter: ['object']
|
||||
}
|
||||
},
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
validator(_: any, value: any) {
|
||||
console.log('validator',value)
|
||||
if (!value?.type) {
|
||||
return Promise.reject('请选择数据类型')
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
}]
|
||||
},
|
||||
width: 100
|
||||
},
|
||||
|
@ -143,16 +172,16 @@ const typeChange = (e: string) => {
|
|||
...props.value,
|
||||
valueType: { ...obj, type: e }
|
||||
});
|
||||
formItemContext.onFieldChange()
|
||||
};
|
||||
|
||||
const valueChange = (_data: any, _type: string) => {
|
||||
console.log(_type, _data)
|
||||
const newData = handleTypeValue(_type, _data)
|
||||
console.log('dataType',{...newData, type: type.value})
|
||||
emit('update:value', {
|
||||
...props.value,
|
||||
valueType: {...newData, type: type.value},
|
||||
});
|
||||
formItemContext.onFieldChange()
|
||||
}
|
||||
|
||||
const { unitOptions } = useUnit(type)
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
<template>
|
||||
<DataTableObject v-model:value="value" :columns="columns" @confirm="confirm">
|
||||
<template #valueType="{ data }">
|
||||
<span>{{ data.record.valueType?.type }}</span>
|
||||
<span>{{ TypeStringMap[data.record.valueType?.type] }}</span>
|
||||
</template>
|
||||
<template #config="{ data }">
|
||||
<ConfigModal v-model:value="data.record" />
|
||||
<ConfigModal v-model:value="data.record.valueType" :showOther="false" />
|
||||
</template>
|
||||
<j-button>
|
||||
<AIcon type="SettingOutlined" />
|
||||
配置
|
||||
</j-button>
|
||||
<ModelButton />
|
||||
</DataTableObject>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="InputParams">
|
||||
<script setup lang="ts" name="ConfigParams">
|
||||
import type { PropType } from 'vue';
|
||||
import {
|
||||
DataTableObject,
|
||||
|
@ -21,6 +18,9 @@ import {
|
|||
import { ValueObject } from '../index'
|
||||
|
||||
import ConfigModal from '@/views/device/components/Metadata/Base/components/ConfigModal.vue'
|
||||
import ModelButton from '@/views/device/components/Metadata/Base/components/ModelButton.vue'
|
||||
import {omit} from "lodash-es";
|
||||
import {TypeStringMap} from "../../columns";
|
||||
|
||||
const columns = [
|
||||
{
|
||||
|
@ -36,13 +36,19 @@ const columns = [
|
|||
const fieldIndex = Number(field[1])
|
||||
const hasId = _dataSource.some((item, index) => item.id === value && fieldIndex !== index)
|
||||
if (hasId) {
|
||||
return Promise.reject('该标识存在')
|
||||
return Promise.reject('该标识已存在')
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
return Promise.reject('请输入标识')
|
||||
}
|
||||
}]
|
||||
},
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
{
|
||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -53,8 +59,10 @@ const columns = [
|
|||
required: true,
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入参数标识名称'
|
||||
}]
|
||||
message: '请输入参数名称'
|
||||
},
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -63,10 +71,19 @@ const columns = [
|
|||
dataIndex: 'valueType',
|
||||
components: {
|
||||
name: ValueObject,
|
||||
props: {
|
||||
filter: ['object']
|
||||
}
|
||||
},
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
validator(_: any, value: any) {
|
||||
console.log('validator',value)
|
||||
if (!value?.type) {
|
||||
return Promise.reject('请选择数据类型')
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '其他配置',
|
||||
|
@ -87,8 +104,8 @@ type Emits = {
|
|||
const emit = defineEmits<Emits>();
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Object as PropType<Record<string, any>>,
|
||||
default: () => {},
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
|
@ -100,35 +117,26 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
const value = ref(props.value.valueType?.properties);
|
||||
const type = computed(() => {
|
||||
return props.value.valueType?.type
|
||||
})
|
||||
|
||||
const change = (v: string) => {
|
||||
emit('update:value', { ...props.value, async: value.value });
|
||||
emit('change', v);
|
||||
};
|
||||
const value = ref(props.value.properties);
|
||||
|
||||
const confirm = (data: any) => {
|
||||
console.log('ConfigParams',data)
|
||||
const newObject = data.map((item) => {
|
||||
const { config, action, _sortIndex, ...extra } = item
|
||||
return extra
|
||||
return omit(item, ['_sortIndex', 'config', 'action'])
|
||||
})
|
||||
|
||||
console.log('ConfigParams',newObject)
|
||||
|
||||
emit('update:value', {
|
||||
...props.value,
|
||||
valueType: {
|
||||
properties: newObject,
|
||||
type: props.value.valueType.type,
|
||||
}
|
||||
properties: newObject,
|
||||
type: 'object',
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newV) => {
|
||||
value.value = props.value.valueType?.properties;
|
||||
value.value = props.value.properties;
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
<template>
|
||||
<DataTableObject :value="dataSource" :columns="columns" :onAdd="addItem" width="700px" @confirm="confirm">
|
||||
<DataTableObject :value="value" :columns="columns" :onAdd="addItem" width="700px" @confirm="confirm">
|
||||
<template #valueType="{ data }">
|
||||
<span>{{ data.record.valueType?.type }}</span>
|
||||
<span>{{ TypeStringMap[data.record.valueType?.type] }}</span>
|
||||
</template>
|
||||
<template #required="{ data }">
|
||||
<span>{{ data.record.expands?.required ? "是": '否' }}</span>
|
||||
</template>
|
||||
<template #config="{ data }">
|
||||
<ConfigModal v-model:value="data.record" />
|
||||
<ConfigModal v-model:value="data.record.valueType" :showOther="false" />
|
||||
</template>
|
||||
<j-button>
|
||||
<AIcon type="SettingOutlined" />
|
||||
配置
|
||||
</j-button>
|
||||
<ModelButton />
|
||||
</DataTableObject>
|
||||
</template>
|
||||
|
||||
|
@ -23,7 +20,8 @@ import {
|
|||
DataTableObject,
|
||||
} from 'jetlinks-ui-components';
|
||||
import { ConstraintSelect, ValueObject } from '../index'
|
||||
|
||||
import {TypeStringMap} from "../../columns";
|
||||
import ModelButton from '@/views/device/components/Metadata/Base/components/ModelButton.vue'
|
||||
|
||||
type Emits = {
|
||||
(e: 'update:value', data: Record<string, any>): void;
|
||||
|
@ -33,8 +31,8 @@ type Emits = {
|
|||
const emit = defineEmits<Emits>();
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Object as PropType<Record<string, any>>,
|
||||
default: () => {},
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
|
@ -68,7 +66,6 @@ const columns = ref([
|
|||
form: {
|
||||
required: true,
|
||||
rules: [
|
||||
{ required: true, message: '请输入标识' },
|
||||
{
|
||||
callback(rule:any,value: any, _dataSource: any[]) {
|
||||
if (value) {
|
||||
|
@ -76,17 +73,30 @@ const columns = ref([
|
|||
const fieldIndex = Number(field[1])
|
||||
const hasId = _dataSource.some((item, index) => item.id === value && fieldIndex !== index)
|
||||
if (hasId) {
|
||||
return Promise.reject('该标识存在')
|
||||
return Promise.reject('该标识已存在')
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
return Promise.reject('请输入标识')
|
||||
}
|
||||
},
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
{
|
||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
{ title: '参数名称', dataIndex: 'name', type: 'text', form: { required: true, rules: [{ required: true, message: '请输入名称'}]} },
|
||||
{
|
||||
title: '参数名称',
|
||||
dataIndex: 'name',
|
||||
type: 'text',
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{ required: true, message: '请输入名称'}, { max: 64, message: '最多可输入64个字符' },]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '填写约束',
|
||||
dataIndex: 'required',
|
||||
|
@ -102,9 +112,18 @@ const columns = ref([
|
|||
dataIndex: 'valueType',
|
||||
components: {
|
||||
name: ValueObject,
|
||||
form: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
validator(_: any, value: any) {
|
||||
console.log('validator',value)
|
||||
if (!value?.type) {
|
||||
return Promise.reject('请选择数据类型')
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
}]
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -118,28 +137,20 @@ const columns = ref([
|
|||
},
|
||||
])
|
||||
|
||||
const dataSource = ref(props.value);
|
||||
// const dataSource = ref(props.value.inputs);
|
||||
|
||||
const change = (v: string) => {
|
||||
emit('update:value', { ...props.value, async: dataSource.value });
|
||||
emit('change', v);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => JSON.stringify(props.value),
|
||||
(newV) => {
|
||||
console.log('inputParams-change')
|
||||
dataSource.value = props.value.inputs;
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
// watch(
|
||||
// () => JSON.stringify(props.value),
|
||||
// (newV) => {
|
||||
// console.log('inputParams-change', props.value.inputs)
|
||||
// dataSource.value = props.value.inputs;
|
||||
// },
|
||||
// { immediate: true },
|
||||
// );
|
||||
|
||||
const confirm = (v: any) => {
|
||||
console.log('inputParams',v)
|
||||
emit('update:value', {
|
||||
...props.value,
|
||||
inputs: v
|
||||
})
|
||||
emit('update:value', v)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -12,74 +12,15 @@
|
|||
v-else-if="type === 'object'"
|
||||
v-model:value="data.properties"
|
||||
placement="topRight"
|
||||
:columns="[
|
||||
{
|
||||
title: '参数标识',
|
||||
dataIndex: 'id',
|
||||
type: 'text',
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
callback(rule:any,value: any, dataSource: any[]) {
|
||||
if (value) {
|
||||
const field = rule.field.split('.')
|
||||
const fieldIndex = Number(field[1])
|
||||
const hasId = dataSource.some((item, index) => item.id === value && fieldIndex !== index)
|
||||
if (hasId) {
|
||||
return Promise.reject('该标识存在')
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
return Promise.reject('请输入标识')
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '参数名称',
|
||||
dataIndex: 'name',
|
||||
type: 'text',
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入参数名称'
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '数据类型',
|
||||
type: 'components',
|
||||
dataIndex: 'valueType',
|
||||
components: {
|
||||
name: Type,
|
||||
},
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请选择数据类型'
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '其他配置',
|
||||
dataIndex: 'config',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
width: 60
|
||||
}
|
||||
]"
|
||||
:columns="columns"
|
||||
@confirm="valueChange"
|
||||
:onAdd="addItem"
|
||||
>
|
||||
<template #valueType="{ data }">
|
||||
<span>{{ data.record.valueType?.type }}</span>
|
||||
<span>{{ TypeStringMap[data.record.valueType?.type] }}</span>
|
||||
</template>
|
||||
<template #config="{ data }">
|
||||
<ConfigModal v-model:value="data.record" />
|
||||
<ConfigModal v-model:value="data.record.valueType" :showOther="false"/>
|
||||
</template>
|
||||
</DataTableObject>
|
||||
<DataTableEnum v-else-if="type === 'enum'" v-model:value="data" @confirm="valueChange"/>
|
||||
|
@ -123,7 +64,7 @@ import {
|
|||
|
||||
import ConfigModal from '@/views/device/components/Metadata/Base/components/ConfigModal.vue'
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import {typeSelectChange, useUnit} from "@/views/device/components/Metadata/Base/columns";
|
||||
import {typeSelectChange, TypeStringMap, useUnit} from "@/views/device/components/Metadata/Base/columns";
|
||||
import Type from './Type.vue'
|
||||
|
||||
const props = defineProps({
|
||||
|
@ -138,7 +79,7 @@ const options = ref<{ label: string; value: string }[]>([]);
|
|||
const emit = defineEmits(['update:value']);
|
||||
|
||||
const type = ref(props.value?.output?.type);
|
||||
const data = ref(cloneDeep(props.value?.output));
|
||||
const data = ref(props.value?.output);
|
||||
|
||||
const { unitOptions } = useUnit(type)
|
||||
|
||||
|
@ -150,6 +91,80 @@ const typeChange = (e: string) => {
|
|||
});
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '参数标识',
|
||||
dataIndex: 'id',
|
||||
type: 'text',
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
callback(rule:any,value: any, dataSource: any[]) {
|
||||
if (value) {
|
||||
const field = rule.field.split('.')
|
||||
const fieldIndex = Number(field[1])
|
||||
const hasId = dataSource.some((item, index) => item.id === value && fieldIndex !== index)
|
||||
if (hasId) {
|
||||
return Promise.reject('该标识已存在')
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
return Promise.reject('请输入标识')
|
||||
}
|
||||
},
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
{
|
||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '参数名称',
|
||||
dataIndex: 'name',
|
||||
type: 'text',
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
required: true,
|
||||
message: '请输入参数名称'
|
||||
},
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '数据类型',
|
||||
type: 'components',
|
||||
dataIndex: 'valueType',
|
||||
components: {
|
||||
name: Type,
|
||||
},
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
validator(_: any, value: any) {
|
||||
console.log('validator',value)
|
||||
if (!value?.type) {
|
||||
return Promise.reject('请选择数据类型')
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '其他配置',
|
||||
dataIndex: 'config',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
width: 60
|
||||
}
|
||||
]
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
() => {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<template>
|
||||
<DataTableTypeSelect
|
||||
v-model:value="myValue"
|
||||
:filter="['object']"
|
||||
@change="change"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<j-button type="link" stype="padding-left: 0;">
|
||||
<AIcon type="SettingOutlined" />
|
||||
配置
|
||||
</j-button>
|
||||
</template>
|
||||
|
||||
<script setup name="ModelButton">
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -11,7 +11,7 @@
|
|||
{{ data.record.range === 'true' ? '范围值' : '固定值'}}
|
||||
</template>
|
||||
<template #value="{data}">
|
||||
{{ data.record.range === 'true' ? data.record.value?.toString() : data.record.value }}
|
||||
{{ data.record.range === 'true' ? data.record.value?.join('-') : data.record.value }}
|
||||
</template>
|
||||
<template #action="{data}">
|
||||
<j-button
|
||||
|
@ -22,7 +22,7 @@
|
|||
</j-button>
|
||||
</template>
|
||||
</j-data-table>
|
||||
<j-button style="width: 100%;margin-top: 16px;" @click="addItem">
|
||||
<j-button style="width: 100%;margin-top: 16px;" @click="addItem" >
|
||||
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||
添加指标值
|
||||
</j-button>
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
<AIcon type="ExclamationCircleOutlined" style="padding-left: 12px;padding-top: 4px;" />
|
||||
</j-tooltip>
|
||||
</template>
|
||||
<Metrics ref="metricsRef" :value="myValue.expands?.metrics" :type="props.value?.valueType?.type"/>
|
||||
<Metrics ref="metricsRef" :value="myValue.metrics" :type="props.type"/>
|
||||
</j-collapse-panel>
|
||||
</j-collapse>
|
||||
|
||||
|
@ -49,10 +49,7 @@
|
|||
</div>
|
||||
|
||||
</template>
|
||||
<j-button>
|
||||
<AIcon type="SettingOutlined" />
|
||||
配置
|
||||
</j-button>
|
||||
<ModelButton />
|
||||
</j-popconfirm-modal>
|
||||
</template>
|
||||
|
||||
|
@ -63,12 +60,17 @@ import {cloneDeep} from "lodash";
|
|||
import {useProductStore} from "store/product";
|
||||
import {useInstanceStore} from "store/instance";
|
||||
import {getMetadataConfig, getMetadataDeviceConfig} from "@/api/device/product";
|
||||
import ModelButton from '@/views/device/components/Metadata/Base/components/ModelButton.vue'
|
||||
import {omit} from "lodash-es";
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: undefined
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -90,7 +92,7 @@ const config = ref<any>([])
|
|||
const configValue = ref(props.value?.expands)
|
||||
|
||||
const showMetrics = computed(() => {
|
||||
return ['int', 'long', 'float', 'double', 'string', 'boolean', 'date'].includes(props.value?.valueType?.type as any)
|
||||
return ['int', 'long', 'float', 'double', 'string', 'boolean', 'date'].includes(props.type as any)
|
||||
})
|
||||
|
||||
const columns = ref([
|
||||
|
@ -114,15 +116,14 @@ const columns = ref([
|
|||
const getConfig = async () => {
|
||||
const record = props.value
|
||||
const id = type === 'product' ? productStore.current?.id : deviceStore.current.id
|
||||
console.log(record.id, id, record.valueType)
|
||||
if(!record.id || !id || !record.valueType.type) return
|
||||
if(!record.id || !id || !record.type) return
|
||||
|
||||
const params: any = {
|
||||
deviceId: id,
|
||||
metadata: {
|
||||
id: record.id,
|
||||
type: 'property',
|
||||
dataType: record.valueType.type,
|
||||
dataType: record.type,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -162,17 +163,12 @@ const confirm = () => {
|
|||
if (metrics) {
|
||||
expands.metrics = metrics
|
||||
}
|
||||
console.log(expands)
|
||||
emit('update:value', {
|
||||
...props.value,
|
||||
expands: {
|
||||
...(props.value.expands || {}),
|
||||
...expands
|
||||
}
|
||||
...expands
|
||||
})
|
||||
resolve(true)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
reject(false)
|
||||
}
|
||||
})
|
||||
|
@ -180,18 +176,16 @@ const confirm = () => {
|
|||
|
||||
const visibleChange = (e: boolean) => {
|
||||
if (e) {
|
||||
configValue.value = omit(props.value?.expands, ['source', 'type', 'metrics', 'required'])
|
||||
configValue.value = omit(props.value, ['source', 'type', 'metrics', 'required'])
|
||||
getConfig()
|
||||
}
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
console.log(props.value)
|
||||
myValue.value = cloneDeep(props.value)
|
||||
}
|
||||
|
||||
watch(() => props.value, () => {
|
||||
console.log(props.value)
|
||||
myValue.value = cloneDeep(props.value)
|
||||
}, {immediate: true, deep: true})
|
||||
|
||||
|
|
|
@ -8,31 +8,32 @@
|
|||
:disabled="disabled"
|
||||
>
|
||||
</j-select>
|
||||
<j-popconfirm-modal
|
||||
v-if="myValue != 'manual'"
|
||||
@confirm="confirm"
|
||||
:bodyStyle="{width: '450px', height: myValue === 'rule' ? '300px' : '80px'}"
|
||||
>
|
||||
<template #content>
|
||||
<j-scrollbar v-if="myValue">
|
||||
<VirtualRule
|
||||
:value="value"
|
||||
:source="myValue"
|
||||
:dataSource="dataSource"
|
||||
ref="virtualRuleRef"
|
||||
/>
|
||||
</j-scrollbar>
|
||||
</template>
|
||||
<j-button :disabled="!myValue" type="link" style="padding: 4px 8px">
|
||||
<AIcon type="EditOutlined" />
|
||||
</j-button>
|
||||
</j-popconfirm-modal>
|
||||
<j-popconfirm-modal
|
||||
v-if="myValue != 'manual'"
|
||||
@confirm="confirm"
|
||||
:bodyStyle="{width: '450px', height: myValue === 'rule' ? '300px' : '80px'}"
|
||||
>
|
||||
<template #content>
|
||||
<j-scrollbar v-if="myValue">
|
||||
<VirtualRule
|
||||
:value="value"
|
||||
:source="myValue"
|
||||
:dataSource="dataSource"
|
||||
ref="virtualRuleRef"
|
||||
/>
|
||||
</j-scrollbar>
|
||||
</template>
|
||||
<j-button :disabled="!myValue" type="link" style="padding: 4px 8px">
|
||||
<AIcon type="EditOutlined" />
|
||||
</j-button>
|
||||
</j-popconfirm-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="MetadataSource">
|
||||
import { isNoCommunity } from '@/utils/utils';
|
||||
import VirtualRule from './VirtualRule/index.vue';
|
||||
import { Form } from 'jetlinks-ui-components'
|
||||
|
||||
const PropertySource: { label: string; value: string }[] = isNoCommunity
|
||||
? [
|
||||
|
@ -82,6 +83,7 @@ const props = defineProps({
|
|||
});
|
||||
|
||||
const emit = defineEmits<Emit>();
|
||||
const formItemContext = Form.useInjectFormItemContext();
|
||||
|
||||
const myValue = ref<SourceType>('');
|
||||
const type = ref<string>('');
|
||||
|
@ -93,20 +95,27 @@ const disabled = computed(() => {
|
|||
return props.noEdit?.length ? props.noEdit.includes(props.value._sortIndex) :false
|
||||
})
|
||||
|
||||
const updateValue = (data: any) => {
|
||||
emit('update:value', {
|
||||
...props.value,
|
||||
expands: {
|
||||
...(props.value?.expands || {}),
|
||||
...data
|
||||
}
|
||||
})
|
||||
formItemContext.onFieldChange()
|
||||
}
|
||||
|
||||
const onChange = (keys: SourceType) => {
|
||||
myValue.value = keys;
|
||||
emit('update:value', {
|
||||
...props.value,
|
||||
expands: {
|
||||
...props.value?.expands,
|
||||
source: keys,
|
||||
type:
|
||||
keys === 'manual'
|
||||
? ['write']
|
||||
: keys === 'rule'
|
||||
? ['report']
|
||||
: [],
|
||||
},
|
||||
updateValue({
|
||||
source: keys,
|
||||
type:
|
||||
keys === 'manual'
|
||||
? ['write']
|
||||
: keys === 'rule'
|
||||
? ['report']
|
||||
: [],
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -116,15 +125,11 @@ const confirm = async () => {
|
|||
reject();
|
||||
});
|
||||
if (data) {
|
||||
const obj = {
|
||||
...props.value,
|
||||
expands: {
|
||||
...props.value?.expands,
|
||||
...data,
|
||||
},
|
||||
};
|
||||
console.log(obj)
|
||||
emit('update:value', obj);
|
||||
console.log(data)
|
||||
updateValue({
|
||||
source: myValue.value,
|
||||
...data
|
||||
});
|
||||
resolve(true);
|
||||
} else {
|
||||
reject()
|
||||
|
|
|
@ -17,12 +17,47 @@ const useMetadata = (type: 'device' | 'product', key?: MetadataType, ): {
|
|||
const _metadataStr = type === 'product' ? productStore.current?.metadata : instanceStore.current.metadata
|
||||
const _metadata = JSON.parse(_metadataStr || '{}')
|
||||
const newMetadata = (key ? _metadata?.[key] || [] : []) as MetadataItem[]
|
||||
console.log(key, _metadata, newMetadata)
|
||||
const indexs = newMetadata.map((item, index) => index)
|
||||
noEdit.value.id = indexs
|
||||
|
||||
const indexKeys = newMetadata.map((item, index) => index)
|
||||
|
||||
noEdit.value.id = indexKeys
|
||||
|
||||
if (key === 'properties') {
|
||||
noEdit.value.source = indexs
|
||||
noEdit.value.source = indexKeys
|
||||
}
|
||||
|
||||
if (type === 'device' && instanceStore.current.productMetadata) {
|
||||
const productMetadata: any = JSON.parse(instanceStore.current.productMetadata)
|
||||
const metaArray = key ? productMetadata[key] : []
|
||||
const productIndexKeys = metaArray?.map((item:any, index: number) => index) || []
|
||||
noEdit.value.id = productIndexKeys
|
||||
noEdit.value.name = productIndexKeys
|
||||
if (key === 'properties') {
|
||||
noEdit.value.valueType = productIndexKeys
|
||||
noEdit.value.expands = productIndexKeys
|
||||
}
|
||||
|
||||
if (key === 'functions') {
|
||||
noEdit.value.async = productIndexKeys
|
||||
noEdit.value.inputs = productIndexKeys
|
||||
noEdit.value.output = productIndexKeys
|
||||
noEdit.value.description = productIndexKeys
|
||||
}
|
||||
|
||||
if (key === 'events') {
|
||||
noEdit.value.expands = productIndexKeys
|
||||
noEdit.value.outInput = productIndexKeys
|
||||
noEdit.value.properties = productIndexKeys
|
||||
noEdit.value.description = productIndexKeys
|
||||
}
|
||||
|
||||
if (key === 'tags') {
|
||||
noEdit.value.valueType = productIndexKeys
|
||||
noEdit.value.readType = productIndexKeys
|
||||
noEdit.value.description = productIndexKeys
|
||||
}
|
||||
}
|
||||
|
||||
return newMetadata
|
||||
})
|
||||
|
||||
|
|
18
yarn.lock
18
yarn.lock
|
@ -3823,10 +3823,22 @@ 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.23, jetlinks-ui-components@^1.0.24:
|
||||
jetlinks-ui-components@^1.0.23:
|
||||
version "1.0.23"
|
||||
resolved "http://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.23.tgz#6bef8fe635867a226afbfee3af6e870e46b0bf54"
|
||||
integrity sha512-TtJJgtvW2aRvfD1BMTOulefPq3BUyfE02UARRF/XMo9GaPzPq7eqDonH4yrvadEd71cMPj2CYvt/A6FdWJu9yA==
|
||||
dependencies:
|
||||
"@vueuse/core" "^9.12.0"
|
||||
"@vueuse/router" "^9.13.0"
|
||||
ant-design-vue "^3.2.15"
|
||||
colorpicker-v3 "^2.10.2"
|
||||
lodash-es "^4.17.21"
|
||||
monaco-editor "^0.35.0"
|
||||
|
||||
jetlinks-ui-components@^1.0.24:
|
||||
version "1.0.24"
|
||||
resolved "http://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.24.tgz#7d651f3d333ad52154f5dbbbcba7f6ed8e85e442"
|
||||
integrity sha512-MtIwdFA2kq3s1rGLExYjjjuit05pjoIURDelUIE4swN3q9qh8QgHcjAOtwEor+UNp333LT2FnSQbMkfXctrqUQ==
|
||||
resolved "http://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.24.tgz#68660b63aea9b4befeaa4c5e3ff121668bb984bb"
|
||||
integrity sha512-09s73oEPKf+TC3KVL4suXrI4FjylJqyB2qXbFQtBOqyK/mSfMN/TJ7mcEoZdycBqAvmv35zTDKXIQIcvAAaitQ==
|
||||
dependencies:
|
||||
"@vueuse/core" "^9.12.0"
|
||||
"@vueuse/router" "^9.13.0"
|
||||
|
|
Loading…
Reference in New Issue