iot-ui-vue/src/views/device/components/Metadata/Base/columns.tsx

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;