From f1aeeaed4392e3ce04fb61e398a66bb826ddad23 Mon Sep 17 00:00:00 2001 From: qiaochuLei <124648559+qiaochuLei@users.noreply.github.com> Date: Mon, 1 Apr 2024 16:22:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9EBacnet=E6=95=B0?= =?UTF-8?q?=E9=87=87=EF=BC=9B=E6=96=B0=E5=A2=9E=E9=80=9A=E9=81=93=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E5=8D=8F=E8=AE=AEBacNet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 场景联动数组条件 * fix: bug#23054、22282 * fix: bug#23085 * feat: 新增Bacnet数采 * fix: bug#23160、23158 * fix: 网管子设备边缘端映射传值 * feat: 新增通道选择协议BacNet * fix: 系统管理背景图上传bug * fix: 校验条件设置为常量 --- src/api/data-collect/collector.ts | 18 +- src/components/RadioCard/index.vue | 5 +- src/store/menu.ts | 2 +- src/utils/consts.ts | 3 +- src/views/DataCollect/Channel/Save/index.vue | 103 +- src/views/DataCollect/Channel/data.ts | 5 + src/views/DataCollect/Channel/index.vue | 2 +- src/views/DataCollect/Channel/type.d.ts | 5 + .../Collector/Point/Save/SaveBACNet.vue | 301 ++++++ .../Collector/Point/Scan/index.vue | 2 +- .../Collector/Point/ScanBacnet/PropertyId.vue | 80 ++ .../Collector/Point/ScanBacnet/Table.vue | 394 +++++++ .../Collector/Point/ScanBacnet/Tree.vue | 244 +++++ .../Collector/Point/ScanBacnet/index.vue | 105 ++ .../Point/components/BatchUpdate/index.vue | 65 +- .../DataCollect/Collector/Point/index.vue | 28 +- .../DataCollect/Collector/Tree/Save/index.vue | 30 +- src/views/DataCollect/Collector/data.ts | 57 +- .../Category/components/modifyModal/index.vue | 11 +- src/views/device/Category/index.vue | 6 + .../device/Instance/Detail/EdgeMap/index.vue | 87 +- .../Detail/Info/components/Config/index.vue | 8 +- .../device/Instance/Detail/Log/index.vue | 5 +- .../Instance/Detail/Running/Event/index.vue | 4 +- .../Detail/Running/Property/Detail/Charts.vue | 53 +- src/views/device/Instance/Detail/index.vue | 6 +- .../Product/Detail/DeviceAccess/index.vue | 5 +- src/views/device/Product/Save/index.vue | 7 +- src/views/device/Product/index.vue | 130 ++- .../components/Metadata/Base/columns.tsx | 6 +- .../components/Metadata/Import/index.vue | 976 +++++++++++------- src/views/edge/Device/index.vue | 7 +- src/views/home/components/PlatformPicCard.vue | 2 +- src/views/link/AccessConfig/Detail/index.vue | 6 +- .../AccessConfig/components/Media/Onvif.vue | 147 +++ .../AccessConfig/components/Media/index.vue | 4 + src/views/link/AccessConfig/index.vue | 22 +- src/views/link/DashBoard/components/Cpu.vue | 1 - src/views/media/Device/Save/SaveProduct.vue | 8 +- src/views/media/Device/Save/index.vue | 179 +++- src/views/media/Device/const.ts | 2 + src/views/media/Device/index.vue | 1 + .../Alarm/Configuration/Save/Scene/index.vue | 15 + .../Alarm/Log/TabComponent/index.vue | 397 ++++--- src/views/rule-engine/Alarm/Log/index.vue | 9 - .../Save/components/DropdownButton/index.less | 4 + .../Save/components/ParamsDropdown/Array.vue | 135 +++ .../components/ParamsDropdown/ArrayItem.vue | 82 ++ .../Save/components/ParamsDropdown/Double.vue | 37 +- .../Save/components/ParamsDropdown/index.ts | 4 +- .../Save/components/ParamsDropdown/index.vue | 1 - .../Save/components/Terms/ParamsItem.vue | 474 +++++---- .../Scene/Save/components/Terms/util.ts | 1 + .../system/Apply/Save/components/EditForm.vue | 13 +- src/views/system/Basis/index.vue | 16 +- src/views/system/Menu/Detail/BasicInfo.vue | 351 +++++-- src/views/system/Menu/Detail/ButtonMange.vue | 3 +- .../Menu/components/ButtonAddDialog.vue | 29 +- .../system/Menu/components/PermissChoose.vue | 8 +- .../Platforms/Api/components/ApiTest.vue | 10 +- .../system/User/components/EditUserDialog.vue | 21 +- src/views/user/Login/index.vue | 47 +- 62 files changed, 3622 insertions(+), 1167 deletions(-) create mode 100644 src/views/DataCollect/Collector/Point/Save/SaveBACNet.vue create mode 100644 src/views/DataCollect/Collector/Point/ScanBacnet/PropertyId.vue create mode 100644 src/views/DataCollect/Collector/Point/ScanBacnet/Table.vue create mode 100644 src/views/DataCollect/Collector/Point/ScanBacnet/Tree.vue create mode 100644 src/views/DataCollect/Collector/Point/ScanBacnet/index.vue create mode 100644 src/views/link/AccessConfig/components/Media/Onvif.vue create mode 100644 src/views/rule-engine/Scene/Save/components/ParamsDropdown/Array.vue create mode 100644 src/views/rule-engine/Scene/Save/components/ParamsDropdown/ArrayItem.vue diff --git a/src/api/data-collect/collector.ts b/src/api/data-collect/collector.ts index b90fe629..df3d219d 100644 --- a/src/api/data-collect/collector.ts +++ b/src/api/data-collect/collector.ts @@ -68,4 +68,20 @@ export const getSnapTypes = () => server.get('/s7/client/s7codecs/list') export const getArea = () => server.get('/s7/client/s7area/list') -export const exportTemplate = (provider: string, format: string) =>server.get(`/data-collect/point/${provider}/template.${format}`, {}, {responseType: 'blob'}) \ No newline at end of file +export const exportTemplate = (provider: string, format: string) =>server.get(`/data-collect/point/${provider}/template.${format}`, {}, {responseType: 'blob'}) + +/** + * BACNet协议扫描对象 + * @param channelId 通道id + * @param instanceNumber 设备实例号 + */ +export const getBacnetObjectList = (channelId: string, instanceNumber: string) => server.get(`/collect/bacnet/${channelId}/${instanceNumber}/objects`); + +/** + * 查询未使用的属性id + * @param data 采集器Id + */ +export const getBacnetPropertyIdNotUse = (data: any) => server.post(`/collect/bacnet/${data.collectorId}/unused/ids`, data) + +/**查询bacnet值类型*/ +export const getBacnetValueType = () => server.get(`/collect/bacnet/value/types`) \ No newline at end of file diff --git a/src/components/RadioCard/index.vue b/src/components/RadioCard/index.vue index e29c8c76..d66d59fb 100644 --- a/src/components/RadioCard/index.vue +++ b/src/components/RadioCard/index.vue @@ -96,6 +96,7 @@ const handleRadio = (item: any) => { display: flex; flex-wrap: wrap; justify-content: space-between; + gap: 24px; .disabled { >div { color: rgba(0, 0, 0, 0.25); @@ -105,7 +106,7 @@ const handleRadio = (item: any) => { } &-item { - width: 49%; + flex:1; height: 70px; padding: 10px 15px; margin-bottom: 12px; @@ -113,7 +114,7 @@ const handleRadio = (item: any) => { border-radius: 2px; display: flex; align-items: center; - gap: 24px; + cursor: pointer; .img { width: 32px; diff --git a/src/store/menu.ts b/src/store/menu.ts index 2e573be3..bbd6e2c3 100644 --- a/src/store/menu.ts +++ b/src/store/menu.ts @@ -40,7 +40,7 @@ const defaultOwnParams = [ column: "options" } ], - type:'or' + type:'and' } ] } diff --git a/src/utils/consts.ts b/src/utils/consts.ts index ac738daf..7763787c 100644 --- a/src/utils/consts.ts +++ b/src/utils/consts.ts @@ -58,5 +58,6 @@ export const protocolList = [ { label: 'MODBUS_TCP', value: 'MODBUS_TCP', alias: 'Modbus/TCP' }, { label: 'COLLECTOR_GATEWAY', value: 'COLLECTOR_GATEWAY', alias: 'GATEWAY' }, { label: 'S7', value: 'snap7', alias: 'snap7' }, - { label: 'IEC104', value: 'iec104', alias: 'IEC104' } + { label: 'IEC104', value: 'iec104', alias: 'IEC104' }, + { label: 'BACNetIp', value: 'BACNetIp', alias: 'BACNet/IP' } ] diff --git a/src/views/DataCollect/Channel/Save/index.vue b/src/views/DataCollect/Channel/Save/index.vue index 69c827fd..b0a11b75 100644 --- a/src/views/DataCollect/Channel/Save/index.vue +++ b/src/views/DataCollect/Channel/Save/index.vue @@ -1,4 +1,4 @@ - @@ -175,7 +287,6 @@ import { Rule } from 'ant-design-vue/lib/form'; import { isNoCommunity } from '@/utils/utils'; import { onlyMessage } from '@/utils/comm'; - const permission = 'system/Menu'; // 路由 const route = useRoute(); @@ -191,7 +302,7 @@ const basicFormRef = ref(); const permissFormRef = ref(); const uploadIcon = ref(); //菜单应用选项 -const appOptions = ref([]) +const appOptions = ref([]); const form = reactive({ data: { name: '', @@ -216,7 +327,9 @@ const form = reactive({ getMenuInfo_api(routeParams.id).then((resp: any) => { form.data = { ...(resp.result as formType), - permissions: resp.result?.permissions ? resp.result.permissions : [], + permissions: resp.result?.permissions + ? resp.result.permissions + : [], accessSupport: resp.result?.accessSupport?.value || 'unsupported', }; @@ -224,25 +337,34 @@ const form = reactive({ }); if (isNoCommunity) { - // 获取关联菜单 - getMenuTree_api({ paging: false,terms:[{terms:[{ - terms:[ - { - value:"%show\":true%", - termType:"like", - column:"options" - } - ] - }]}]}).then((resp: any) => { - form.treeData = resp.result; - }); - // 获取资产类型 - getAssetsType_api().then((resp: any) => { - form.assetsType = resp.result.map((item: any) => ({ - label: item.name, - value: item.id, - })); - }); + // 获取关联菜单 + getMenuTree_api({ + paging: false, + terms: [ + { + terms: [ + { + terms: [ + { + value: '%show":true%', + termType: 'like', + column: 'options', + }, + ], + }, + ], + }, + ], + }).then((resp: any) => { + form.treeData = resp.result; + }); + // 获取资产类型 + getAssetsType_api().then((resp: any) => { + form.assetsType = resp.result.map((item: any) => ({ + label: item.name, + value: item.id, + })); + }); } }, checkCode: async (_rule: Rule, value: string): Promise => { @@ -270,7 +392,7 @@ const form = reactive({ const api = routeParams.id ? saveMenuInfo_api : addMenuInfo_api; form.saveLoading = true; const accessSupportValue = form.data.accessSupport; - const params:any = { + const params: any = { ...form.data, owner: 'iot', options: { show: true }, @@ -280,8 +402,8 @@ const form = reactive({ accessSupportValue === 'unsupported' ? '不支持' : accessSupportValue === 'support' - ? '支持' - : '间接控制', + ? '支持' + : '间接控制', }, }; api(params) @@ -302,15 +424,20 @@ const form = reactive({ }) .finally(() => (form.saveLoading = false)); }) - .catch((err) => { }); + .catch((err) => {}); }, }); form.init(); +const checkCh = async (_rule: Rule, value: string) => { + if (/[\u4e00-\u9fa5]/.test(value)) + return Promise.reject('编码不能包含中文'); + else return Promise.resolve(''); +}; const choseIcon = (typeStr: string) => { form.data.icon = typeStr; uploadIcon.value?.clearValidate(); -} +}; // 弹窗 const dialogVisible = ref(false); diff --git a/src/views/system/Menu/Detail/ButtonMange.vue b/src/views/system/Menu/Detail/ButtonMange.vue index 038ef3ea..52b84799 100644 --- a/src/views/system/Menu/Detail/ButtonMange.vue +++ b/src/views/system/Menu/Detail/ButtonMange.vue @@ -55,6 +55,7 @@ :menu-info="menuInfo" :mode="dialogTitle" :data="selectItem" + :menuData="table.tableData" @confirm="table.getList" /> @@ -84,8 +85,6 @@ const openDialog = (mode: '查看' | '新增' | '编辑', row: object) => { if (!routeParams.id && !paramsId.value) { return onlyMessage('请先新增菜单基本信息', 'warning'); } - console.log(3); - selectItem.value = { ...row }; dialogTitle.value = mode; dialogVisible.value = true; diff --git a/src/views/system/Menu/components/ButtonAddDialog.vue b/src/views/system/Menu/components/ButtonAddDialog.vue index bf927177..6b474c5c 100644 --- a/src/views/system/Menu/components/ButtonAddDialog.vue +++ b/src/views/system/Menu/components/ButtonAddDialog.vue @@ -15,6 +15,7 @@ :rules="[ { required: true, message: '请输入编码' }, { max: 64, message: '最多可输入64个字符' }, + { validator: validateIdRepeat, trigger: 'blur' }, ]" > - + ; + default: []; + }; }>(); const loading = ref(false); @@ -126,7 +131,7 @@ const initForm = { name: '', id: '', permissions: [], - describe: '', + description: '', } as formType; const formRef = ref(); const form = reactive({ @@ -141,12 +146,24 @@ const codeOptions = [ { label: 'delete', value: 'delete', message: '删除' }, { label: 'update', value: 'update', message: '更新' }, ]; - +const validateIdRepeat = (rule: any, val: any) => { + if (props.mode === '编辑') { + return Promise.resolve(''); + } + const isRepeat = props.menuData.find((i: any) => { + return i.id === val; + }); + if (isRepeat) { + return Promise.reject('编码重复'); + } else { + return Promise.resolve(''); + } +}; type formType = { name: string; id: string; permissions: any[]; - describe: string; + description: string; }; diff --git a/src/views/system/Menu/components/PermissChoose.vue b/src/views/system/Menu/components/PermissChoose.vue index f62e7e3c..03337ed5 100644 --- a/src/views/system/Menu/components/PermissChoose.vue +++ b/src/views/system/Menu/components/PermissChoose.vue @@ -25,9 +25,9 @@ v-model:checked="rowItem.checkAll" :indeterminate="rowItem.indeterminate" @change="() => permission.selectAllOpions(rowItem)" - :disabled="props.disabled" + :disabled="props.disabled || rowItem.disabled" > - {{ rowItem.name }} + {{ rowItem.name }} @@ -35,7 +35,7 @@ v-model:value="rowItem.checkedList" :options="rowItem.options" @change="((checkValue:string[])=>permission.selectOption(rowItem, checkValue))" - :disabled="props.disabled" + :disabled="props.disabled || rowItem.disabled" /> @@ -177,6 +177,7 @@ const permission = reactive({ checked.actions.length < item.actions.length) || false, options, + disabled: item.status === 0, }; }) as permissionType[]; @@ -192,6 +193,7 @@ type permissionType = { checkAll: boolean; indeterminate: boolean; options: any[]; + disabled: boolean; }; type paramsType = { paging: boolean; diff --git a/src/views/system/Platforms/Api/components/ApiTest.vue b/src/views/system/Platforms/Api/components/ApiTest.vue index 891c4e44..23afd4d0 100644 --- a/src/views/system/Platforms/Api/components/ApiTest.vue +++ b/src/views/system/Platforms/Api/components/ApiTest.vue @@ -108,7 +108,7 @@ (); +const method = ref() const requestBody = reactive({ tableColumns: [ { @@ -199,9 +200,10 @@ let schema: any = {}; const refStr = ref(''); const init = () => { - if (!props.selectApi.requestBody) return; - schema = props.selectApi.requestBody.content['application/json'].schema; - refStr.value = schema.$ref || schema?.items?.$ref; + method.value = props.selectApi.method + // if (!props.selectApi.requestBody) return; + // schema = props.selectApi.requestBody.content['application/json'].schema; + // refStr.value = schema.$ref || schema?.items?.$ref; }; init(); diff --git a/src/views/system/User/components/EditUserDialog.vue b/src/views/system/User/components/EditUserDialog.vue index 2cc59751..1b7a074a 100644 --- a/src/views/system/User/components/EditUserDialog.vue +++ b/src/views/system/User/components/EditUserDialog.vue @@ -142,14 +142,16 @@ @@ -267,15 +269,9 @@ const form = reactive({ data: {} as formType, rules: { - checkCh: (_rule:Rule,value:string): Promise => - new Promise((resolve,reject) => { - if (/[\u4e00-\u9fa5]/.test(value)) return reject('用户名不能包含中文'); - else return resolve('') - }), checkUserName: (_rule: Rule, value: string): Promise => new Promise((resolve, reject) => { if (props.type === 'edit') return resolve(''); - if (!value) return reject('请输入用户名'); else if (value.length > 64) return reject('最多可输入64个字符'); validateField_api('username', value).then((resp: any): any => { resp.result.passed @@ -397,7 +393,10 @@ const form = reactive({ }; }, }); - +const checkCh = async(_rule:Rule,value:string) => { + if (/[\u4e00-\u9fa5]/.test(value)) return Promise.reject('用户名不能包含中文'); + else return Promise.resolve('') + } const dealRoleList = (data:any) =>{ return data.map((item:any)=>{ return { diff --git a/src/views/user/Login/index.vue b/src/views/user/Login/index.vue index 662b3bcf..7b74dc8c 100644 --- a/src/views/user/Login/index.vue +++ b/src/views/user/Login/index.vue @@ -118,7 +118,7 @@
@@ -133,6 +133,9 @@ />
+
+ 查看更多 +
@@ -158,6 +161,31 @@ + +
+
+ + + {{ item.name }} + +
+
+