668 lines
18 KiB
TypeScript
668 lines
18 KiB
TypeScript
import { ColumnProps } from "ant-design-vue/es/table";
|
|
import { DataType, Source, InputParams, OtherSetting, OutputParams, ConfigParams, TagsType } from './components'
|
|
import SelectColumn from './components/Events/SelectColumn.vue';
|
|
import AsyncSelect from './components/Function/AsyncSelect.vue';
|
|
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, pick} from "lodash-es";
|
|
import { message } from 'jetlinks-ui-components'
|
|
import { onlyMessage } from "@/utils/comm";
|
|
import {cloneDeep} from "lodash";
|
|
interface DataTableColumnProps extends ColumnProps {
|
|
type?: string,
|
|
components?: {
|
|
name?: any
|
|
[key: string]: any
|
|
}
|
|
form?: {
|
|
rules?: any[]
|
|
[key: string]: any
|
|
},
|
|
options?: any[]
|
|
doubleClick?: (record: any, index: number, dataIndex: string) => boolean
|
|
control?: (newValue: any, oldValue: any) => Boolean
|
|
}
|
|
|
|
const SourceMap = {
|
|
device: '设备',
|
|
manual: '手动',
|
|
rule: '规则',
|
|
};
|
|
|
|
const type = {
|
|
read: '读',
|
|
write: '写',
|
|
report: '上报',
|
|
};
|
|
|
|
export const validatorConfig = (value: any, isObject: boolean = false) => {
|
|
|
|
console.log(value)
|
|
|
|
if (!value) {
|
|
return Promise.resolve()
|
|
}
|
|
|
|
if (value.type === 'enum' && !value.elements?.length) {
|
|
return Promise.reject('请添加枚举项')
|
|
}
|
|
if (value.type === 'array' && !value.elementType?.type) {
|
|
return Promise.reject('请选择元素类型')
|
|
}
|
|
|
|
if (isObject && value.type === 'object' && !value.properties?.length) {
|
|
return Promise.reject('请添加参数')
|
|
}
|
|
|
|
if (value.type === 'file' && (!value.fileType || !Object.keys(value.fileType).length)) {
|
|
return Promise.reject('请选择文件类型')
|
|
}
|
|
|
|
return Promise.resolve()
|
|
}
|
|
|
|
export const handleTypeValue = (type:string, value: any = {}) => {
|
|
let obj: any = {}
|
|
switch (type) {
|
|
case 'array':
|
|
obj.elementType = value
|
|
break;
|
|
case 'object':
|
|
obj.properties = (value || []).map((item: any) => {
|
|
return omit(item, ['config', 'action', '_sortIndex'])
|
|
})
|
|
break;
|
|
case 'int':
|
|
case 'long':
|
|
obj.unit = value
|
|
break;
|
|
case 'file':
|
|
obj.fileType = value
|
|
break;
|
|
case 'date':
|
|
obj.format = value
|
|
break;
|
|
case 'string':
|
|
obj.maxLength = JSON.stringify(value) === '{}' ? undefined : value
|
|
case 'password':
|
|
obj.maxLength = JSON.stringify(value) === '{}' ? undefined : value
|
|
break;
|
|
default:
|
|
obj = value
|
|
break;
|
|
}
|
|
return obj
|
|
}
|
|
|
|
export const typeSelectChange = (type: string) => {
|
|
let obj: any = {}
|
|
switch (type) {
|
|
case 'array':
|
|
obj.elementType = {}
|
|
break;
|
|
case 'object':
|
|
obj.properties = []
|
|
break;
|
|
case 'enum':
|
|
obj.elements = []
|
|
break;
|
|
case 'float':
|
|
case 'double':
|
|
obj.scale = 2
|
|
obj.unit = undefined
|
|
break;
|
|
case 'int':
|
|
case 'long':
|
|
obj.unit = undefined
|
|
break;
|
|
case 'file':
|
|
obj.fileType = undefined
|
|
break;
|
|
case 'date':
|
|
obj.format = undefined
|
|
break;
|
|
case 'string':
|
|
case 'password':
|
|
obj.maxLength = undefined
|
|
break;
|
|
case 'boolean':
|
|
obj.trueText = '是'
|
|
obj.trueValue = 'true'
|
|
obj.falseText = '否'
|
|
obj.falseValue = 'false'
|
|
break;
|
|
}
|
|
return obj
|
|
}
|
|
|
|
const isExtendsProduct = (id: string, productKeys: string, type: string) => {
|
|
if (!id) return false
|
|
const vailKeys = productKeys[type] || []
|
|
if (vailKeys.includes(id)) {
|
|
onlyMessage('继承自产品物模型的数据不支持修改', 'warning')
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
export const useColumns = (type?: MetadataType, target?: 'device' | 'product', noEdit?: Ref<any>, productNoEdit?: Ref<any>) => {
|
|
|
|
const BaseColumns: DataTableColumnProps[] = [
|
|
{
|
|
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: '标识只能由数字、字母、下划线、中划线组成',
|
|
},
|
|
]
|
|
},
|
|
doubleClick(record) {
|
|
if (isExtendsProduct(record.id, productNoEdit?.value, 'id')) {
|
|
return false
|
|
}
|
|
const ids = (noEdit?.value?.id || []) as any[]
|
|
return !ids.includes(record.id)
|
|
}
|
|
},
|
|
{
|
|
title: '名称',
|
|
dataIndex: 'name',
|
|
width: 300,
|
|
type: 'text',
|
|
form: {
|
|
required: true,
|
|
rules: [
|
|
{
|
|
required: true,
|
|
message: '请输入名称'
|
|
},
|
|
{ max: 64, message: '最多可输入64个字符' },
|
|
]
|
|
// rules:[{
|
|
// callback(rule:any,value: any, dataSource: any[]) {
|
|
// console.log(rule,value,dataSource,123)
|
|
// return value
|
|
// }
|
|
// }]
|
|
},
|
|
doubleClick(record) {
|
|
if (isExtendsProduct(record.id, productNoEdit?.value, 'name')) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
},
|
|
];
|
|
|
|
const EventColumns: DataTableColumnProps[] = BaseColumns.concat([
|
|
{
|
|
title: '事件级别',
|
|
dataIndex: 'expands',
|
|
type: 'components',
|
|
components: {
|
|
name: SelectColumn,
|
|
props: {
|
|
options: EventLevel
|
|
}
|
|
},
|
|
doubleClick(record) {
|
|
if (isExtendsProduct(record.id, productNoEdit?.value, 'expands')) {
|
|
return false
|
|
}
|
|
return true
|
|
},
|
|
control(newValue, oldValue) {
|
|
return newValue.expands.level !== oldValue?.expands?.level
|
|
},
|
|
},
|
|
{
|
|
title: '输出参数',
|
|
dataIndex: 'outInput',
|
|
},
|
|
{
|
|
title: '配置参数',
|
|
dataIndex: 'properties',
|
|
width: 100,
|
|
form: {
|
|
required: true,
|
|
rules: [{
|
|
callback(rule: any, value: any, dataSource: any[]) {
|
|
const field = rule.field.split('.')
|
|
const fieldIndex = Number(field[1])
|
|
const record = dataSource.find((item, index) => index === fieldIndex)
|
|
|
|
if (!record.valueType.properties.length) {
|
|
return Promise.reject('请添加配置参数')
|
|
}
|
|
|
|
return Promise.resolve()
|
|
}
|
|
}]
|
|
},
|
|
control(newValue, oldValue) {
|
|
if (newValue && !oldValue) {
|
|
return true
|
|
} else if (newValue && oldValue) {
|
|
return JSON.stringify(newValue.valueType) !== JSON.stringify(oldValue.valueType)
|
|
}
|
|
return false
|
|
},
|
|
},
|
|
{
|
|
title: '说明',
|
|
dataIndex: 'description',
|
|
type: 'text',
|
|
form: {
|
|
rules: [
|
|
{ max: 20, message: '最多可输入20个字符' },
|
|
]},
|
|
doubleClick(record) {
|
|
if (isExtendsProduct(record.id, productNoEdit?.value, 'description')) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
},
|
|
{
|
|
title: '操作',
|
|
dataIndex: 'action',
|
|
width: 120
|
|
}
|
|
]);
|
|
|
|
const FunctionColumns: DataTableColumnProps[] = BaseColumns.concat([
|
|
{
|
|
title: '是否异步',
|
|
dataIndex: 'async',
|
|
type: 'components',
|
|
components: {
|
|
name: AsyncSelect,
|
|
props: {
|
|
options: [
|
|
{ label: '是', value: true },
|
|
{ label: '否', value: false }
|
|
]
|
|
}
|
|
},
|
|
doubleClick(record) {
|
|
return !isExtendsProduct(record.id, productNoEdit?.value, 'async');
|
|
|
|
},
|
|
control(newValue, oldValue) {
|
|
return newValue.async !== oldValue?.async
|
|
},
|
|
},
|
|
{
|
|
title: '输入参数',
|
|
dataIndex: 'inputs',
|
|
width: 120,
|
|
// form: {
|
|
// required: true,
|
|
// rules: [{
|
|
// callback(rule:any,value: any, dataSource: any[]) {
|
|
// const field = rule.field.split('.')
|
|
// const fieldIndex = Number(field[1])
|
|
//
|
|
// const values = dataSource.find((item, index) => index === fieldIndex)
|
|
//
|
|
// return validatorConfig({
|
|
// type: 'object',
|
|
// properties: values.inputs
|
|
// }, true)
|
|
// }
|
|
// }]
|
|
// },
|
|
control(newValue, oldValue) {
|
|
if (newValue && !oldValue) {
|
|
return true
|
|
} else if (newValue && oldValue) {
|
|
return JSON.stringify(newValue.inputs) !== JSON.stringify(oldValue.inputs)
|
|
}
|
|
return false
|
|
},
|
|
},
|
|
{
|
|
title: '输出参数',
|
|
dataIndex: 'output',
|
|
type: 'components',
|
|
components: {
|
|
name: OutputParams
|
|
},
|
|
form: {
|
|
rules: [{
|
|
callback(rule:any,value: any, dataSource: any[]) {
|
|
const field = rule.field.split('.')
|
|
const fieldIndex = Number(field[1])
|
|
const values = dataSource.find((item, index) => index === fieldIndex)
|
|
return validatorConfig(values?.output)
|
|
}
|
|
}]
|
|
},
|
|
doubleClick(record) {
|
|
return !isExtendsProduct(record.id, productNoEdit?.value, 'output');
|
|
},
|
|
control(newValue, oldValue) {
|
|
if (newValue && !oldValue) {
|
|
return true
|
|
} else if (newValue && oldValue) {
|
|
return JSON.stringify(newValue.output) !== JSON.stringify(oldValue.output)
|
|
}
|
|
return false
|
|
},
|
|
},
|
|
{
|
|
title: '说明',
|
|
dataIndex: 'description',
|
|
type: 'text',
|
|
form: {
|
|
rules: [
|
|
{ max: 20, message: '最多可输入20个字符' },
|
|
]},
|
|
doubleClick(record) {
|
|
if (isExtendsProduct(record.id, productNoEdit?.value, 'description')) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
},
|
|
{
|
|
title: '操作',
|
|
dataIndex: 'action',
|
|
width: 120
|
|
}
|
|
// {
|
|
// title: '读写类型',
|
|
// dataIndex: 'expands',
|
|
// render: (text: any) => (text?.type || []).map((item: string | number) => <Tag>{type[item]}</Tag>),
|
|
// },
|
|
]);
|
|
|
|
const PropertyColumns: DataTableColumnProps[] = BaseColumns.concat([
|
|
{
|
|
title: '数据类型',
|
|
dataIndex: 'valueType',
|
|
type: 'components',
|
|
components: {
|
|
name: DataType
|
|
},
|
|
form: {
|
|
required: true,
|
|
rules: [{
|
|
validator(_: any, value: any) {
|
|
if (!value?.type) {
|
|
return Promise.reject('请选择数据类型')
|
|
}
|
|
return validatorConfig(value, true)
|
|
}
|
|
}]
|
|
},
|
|
width: 230,
|
|
doubleClick(record) {
|
|
if (isExtendsProduct(record.id, productNoEdit?.value, 'valueType')) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
},
|
|
{
|
|
title: '属性来源',
|
|
dataIndex: 'expands',
|
|
type: 'components',
|
|
components: {
|
|
name: Source,
|
|
props: {
|
|
noEdit: noEdit?.value?.source || [],
|
|
target: target,
|
|
productNoEdit: productNoEdit?.value
|
|
}
|
|
},
|
|
doubleClick(record){
|
|
if (record.expands.source === 'rule') {
|
|
return true
|
|
}
|
|
return !isExtendsProduct(record.id, productNoEdit?.value, 'expands')
|
|
},
|
|
form: {
|
|
required: true,
|
|
rules: [
|
|
{
|
|
callback: async (rule: any, value: any, dataSource: any[]) => {
|
|
const field = rule.field.split('.')
|
|
const fieldIndex = Number(field[1])
|
|
|
|
const values = dataSource.find((item, index) => index === fieldIndex)
|
|
const virtualRule = values.expands?.virtualRule
|
|
const source = value.source
|
|
console.log(source, value)
|
|
if (source) {
|
|
if (source === 'device' && !value.type?.length) {
|
|
return Promise.reject('请选择读写类型');
|
|
} else if( source === 'rule' && !virtualRule){
|
|
return Promise.reject('请配置规则');
|
|
}
|
|
|
|
return Promise.resolve()
|
|
}
|
|
|
|
return Promise.reject('请选择属性来源');
|
|
}
|
|
},
|
|
]
|
|
},
|
|
control(newValue, oldValue) {
|
|
if (newValue && !oldValue) {
|
|
return true
|
|
} else if (newValue && oldValue) {
|
|
const keys = ['source', 'type', 'virtualRule']
|
|
const newObj = pick(cloneDeep(newValue.expands), keys)
|
|
const oldObj = pick(cloneDeep(oldValue.expands), keys)
|
|
return JSON.stringify(newObj) !== JSON.stringify(oldObj)
|
|
}
|
|
return false
|
|
},
|
|
width: 150
|
|
},
|
|
{
|
|
title: '其它配置',
|
|
dataIndex: 'other',
|
|
width: 100,
|
|
control(newValue, oldValue) {
|
|
if (!oldValue) {
|
|
return true
|
|
} else if (newValue && oldValue) {
|
|
// 仅留下存储和指标值
|
|
const keys = ['source', 'type', 'virtualRule', 'required']
|
|
const newObj = omit(cloneDeep(newValue.expands), keys)
|
|
const oldObj = omit(cloneDeep(oldValue.expands), keys)
|
|
return JSON.stringify(newObj) !== JSON.stringify(oldObj)
|
|
}
|
|
return false
|
|
},
|
|
},
|
|
{
|
|
title: '操作',
|
|
dataIndex: 'action',
|
|
width: 120
|
|
}
|
|
]);
|
|
|
|
const TagColumns: DataTableColumnProps[] = BaseColumns.concat([
|
|
{
|
|
title: '数据类型',
|
|
dataIndex: 'valueType',
|
|
type: 'components',
|
|
components: {
|
|
name: DataType,
|
|
},
|
|
form: {
|
|
required: true,
|
|
rules: [{
|
|
validator(_: any, value: any) {
|
|
if (!value?.type) {
|
|
return Promise.reject('请选择数据类型')
|
|
}
|
|
return validatorConfig(value, true)
|
|
}
|
|
}]
|
|
},
|
|
doubleClick(record) {
|
|
if (isExtendsProduct(record.id, productNoEdit?.value, 'valueType')) {
|
|
return false
|
|
}
|
|
return true
|
|
},
|
|
control(newValue, oldValue) {
|
|
if (newValue && !oldValue) {
|
|
return true
|
|
} else if (newValue && oldValue) {
|
|
return JSON.stringify(newValue.valueType) !== JSON.stringify(oldValue.valueType)
|
|
}
|
|
return false
|
|
},
|
|
},
|
|
{
|
|
title: '读写类型',
|
|
dataIndex: 'readType',
|
|
type: 'components',
|
|
components: {
|
|
name: TagsType
|
|
},
|
|
doubleClick(record) {
|
|
if (isExtendsProduct(record.id, productNoEdit?.value, 'readType')) {
|
|
return false
|
|
}
|
|
return true
|
|
},
|
|
form: {
|
|
required: true,
|
|
rules: [
|
|
{
|
|
callback(rule:any,value: any, dataSource: any[]) {
|
|
const field = rule.field.split('.')
|
|
const fieldIndex = Number(field[1])
|
|
const values = dataSource.find((item, index) => index === fieldIndex)
|
|
if (!values?.expands?.type?.length) {
|
|
return Promise.reject('请选择读写类型')
|
|
}
|
|
return Promise.resolve()
|
|
}
|
|
}]
|
|
},
|
|
control(newValue, oldValue) {
|
|
if (newValue && !oldValue) {
|
|
return true
|
|
} else if (newValue && oldValue) {
|
|
return JSON.stringify(newValue.expands) !== JSON.stringify(oldValue.expands)
|
|
}
|
|
return false
|
|
},
|
|
},
|
|
{
|
|
title: '说明',
|
|
dataIndex: 'description',
|
|
type: 'text',
|
|
form: {
|
|
rules: [
|
|
{ max: 20, message: '最多可输入20个字符' },
|
|
]},
|
|
doubleClick(record) {
|
|
if (isExtendsProduct(record.id, productNoEdit?.value, 'description')) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
},
|
|
{
|
|
title: '操作',
|
|
dataIndex: 'action',
|
|
width: 120
|
|
}
|
|
]);
|
|
|
|
const columns = ref<any[]>([])
|
|
|
|
watch(() => JSON.stringify(noEdit!.value), () => {
|
|
switch(type) {
|
|
case 'properties':
|
|
columns.value = PropertyColumns
|
|
break ;
|
|
case 'events':
|
|
columns.value = EventColumns
|
|
break ;
|
|
case 'tags':
|
|
columns.value = TagColumns
|
|
break ;
|
|
case 'functions':
|
|
columns.value = FunctionColumns
|
|
break ;
|
|
}
|
|
}, { immediate: true })
|
|
|
|
return {columns}
|
|
}
|
|
|
|
export const useUnit = (type: Ref<string>) => {
|
|
const unitOptions = ref<Array<{ label: string, value: any }>>([])
|
|
|
|
watch(() => 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);
|
|
// MetadataMapping.set('tags', TagColumns);
|
|
// MetadataMapping.set('functions', FunctionColumns);
|
|
//
|
|
// export default MetadataMapping;
|