Commit 9acb4413b37369782b246327a21bdabbbf2649ec

Authored by xp.Huang
2 parents 70490d49 ab9eacc3

Merge branch 'fix/data-flow-transfer-table' into 'main_dev'

fix: 重构数据流转设备穿梭表格

See merge request yunteng/thingskit-front!924
@@ -18,7 +18,7 @@ @@ -18,7 +18,7 @@
18 import { Button } from 'ant-design-vue'; 18 import { Button } from 'ant-design-vue';
19 import { ref, unref } from 'vue'; 19 import { ref, unref } from 'vue';
20 20
21 - const props = defineProps({ 21 + defineProps({
22 saveContent: { 22 saveContent: {
23 type: Function, 23 type: Function,
24 }, 24 },
@@ -28,7 +28,7 @@ @@ -28,7 +28,7 @@
28 28
29 const disabled = ref<boolean>(false); 29 const disabled = ref<boolean>(false);
30 const [register, { validateFields, setFieldsValue, resetFields, setProps }] = useForm({ 30 const [register, { validateFields, setFieldsValue, resetFields, setProps }] = useForm({
31 - schemas: modeForm(props.saveContent, unref(disabled)), 31 + schemas: modeForm(unref(disabled)),
32 ...modelFormPublicConfig, 32 ...modelFormPublicConfig,
33 }); 33 });
34 34
@@ -37,7 +37,7 @@ @@ -37,7 +37,7 @@
37 import { BasicModal, useModalInner } from '/@/components/Modal'; 37 import { BasicModal, useModalInner } from '/@/components/Modal';
38 import { add } from '/@/components/Form/src/componentMap'; 38 import { add } from '/@/components/Form/src/componentMap';
39 import TransferModal from '/@/components/Form/src/components/TransferModal.vue'; 39 import TransferModal from '/@/components/Form/src/components/TransferModal.vue';
40 - import TransferTableModal from '/@/components/Form/src/components/TransferTableModal.vue'; 40 + import TransferTableModal from './TransferTableModal.vue';
41 import { DataFlowMethod, DataFlowParams } from './index'; 41 import { DataFlowMethod, DataFlowParams } from './index';
42 import { stepConfig, removeFieldByModeForm } from './config'; 42 import { stepConfig, removeFieldByModeForm } from './config';
43 import { postAddConvertApi } from '/@/api/datamanager/dataManagerApi'; 43 import { postAddConvertApi } from '/@/api/datamanager/dataManagerApi';
  1 +import { h, unref } from 'vue';
  2 +import { findDictItemByCode } from '/@/api/system/dict';
  3 +import { FETCH_SETTING } from '/@/components/Table/src/const';
  4 +import { DeviceStatusEnum, DeviceStatusNameEnum, DeviceTypeNameEnum } from './enum';
  5 +import { Tag } from 'ant-design-vue';
  6 +import { BasicColumn, BasicTableProps, FormSchema } from '/@/components/Table';
  7 +import { useClipboard } from '@vueuse/core';
  8 +import { useMessage } from '/@/hooks/web/useMessage';
  9 +
  10 +export const deviceTableFormSchema: FormSchema[] = [
  11 + {
  12 + field: 'name',
  13 + label: '设备名称',
  14 + component: 'Input',
  15 + colProps: { span: 9 },
  16 + componentProps: {
  17 + placeholder: '请输入设备名称',
  18 + },
  19 + },
  20 + {
  21 + field: 'deviceType',
  22 + label: '设备类型',
  23 + component: 'ApiSelect',
  24 + colProps: { span: 9 },
  25 + componentProps: {
  26 + placeholder: '请选择设备类型',
  27 + api: findDictItemByCode,
  28 + params: {
  29 + dictCode: 'device_type',
  30 + },
  31 + labelField: 'itemText',
  32 + valueField: 'itemValue',
  33 + },
  34 + },
  35 +];
  36 +
  37 +const { copied, copy } = useClipboard({ legacy: true });
  38 +
  39 +const { createMessage } = useMessage();
  40 +
  41 +export const deviceTableColumn: BasicColumn[] = [
  42 + {
  43 + title: '状态',
  44 + dataIndex: 'deviceState',
  45 + customRender: ({ text }) => {
  46 + return h(
  47 + Tag,
  48 + {
  49 + color:
  50 + text === DeviceStatusEnum.INACTIVE
  51 + ? 'warning'
  52 + : text === DeviceStatusEnum.OFFLINE
  53 + ? 'error'
  54 + : 'success',
  55 + },
  56 + () => DeviceStatusNameEnum[text]
  57 + );
  58 + },
  59 + },
  60 + {
  61 + title: '别名/设备名称',
  62 + dataIndex: 'name',
  63 + customRender: ({ record }) => {
  64 + return h('div', [
  65 + h(
  66 + 'div',
  67 + {
  68 + class: 'cursor-pointer',
  69 + onClick: async () => {
  70 + await copy(record.name);
  71 + if (unref(copied)) createMessage.success('复制成功~');
  72 + },
  73 + },
  74 + [
  75 + record.alias && h('div', { class: 'truncate' }, record.alias),
  76 + h('div', { class: 'text-blue-400 truncate' }, record.name),
  77 + ]
  78 + ),
  79 + ]);
  80 + },
  81 + },
  82 + {
  83 + title: '设备类型',
  84 + dataIndex: 'deviceType',
  85 + customRender: ({ text }) => {
  86 + return h(Tag, { color: 'success' }, () => DeviceTypeNameEnum[text]);
  87 + },
  88 + },
  89 + {
  90 + title: '所属产品',
  91 + dataIndex: 'deviceProfile.name',
  92 + },
  93 + {
  94 + title: '所属组织',
  95 + dataIndex: 'organizationDTO.name',
  96 + },
  97 +];
  98 +
  99 +export const TransferTableProps: BasicTableProps = {
  100 + formConfig: {
  101 + layout: 'inline',
  102 + labelWidth: 80,
  103 + schemas: deviceTableFormSchema,
  104 + actionColOptions: { span: 6 },
  105 + },
  106 + size: 'small',
  107 + maxHeight: 240,
  108 + useSearchForm: true,
  109 + columns: deviceTableColumn,
  110 + showIndexColumn: false,
  111 + fetchSetting: FETCH_SETTING,
  112 +} as BasicTableProps;
  1 +<script lang="ts" setup>
  2 + import { Button, Tabs, Tag } from 'ant-design-vue';
  3 + import { remove, uniqBy, cloneDeep } from 'lodash';
  4 + import { computed, nextTick, onMounted, ref, unref, toRaw } from 'vue';
  5 + import {
  6 + deviceTableColumn,
  7 + deviceTableFormSchema,
  8 + TransferTableProps,
  9 + } from './TransferTableModal.config';
  10 + import { devicePage } from '/@/api/device/deviceManager';
  11 + import { DeviceModel as RawDeviceModal } from '/@/api/device/model/deviceModel';
  12 + import { BasicModal, useModal } from '/@/components/Modal';
  13 + import { BasicTable, useTable } from '/@/components/Table';
  14 + import { FETCH_SETTING } from '/@/components/Table/src/const';
  15 + import { useDesign } from '/@/hooks/web/useDesign';
  16 + import { isFunction } from '/@/utils/is';
  17 +
  18 + interface DeviceModel extends RawDeviceModal {
  19 + disabled?: boolean;
  20 + }
  21 +
  22 + const props = withDefaults(
  23 + defineProps<{
  24 + getPendingTableParams: (params: Recordable) => any;
  25 + getSelectedTableParams: (params: Recordable) => any;
  26 + value?: (Recordable & DeviceModel)[];
  27 + maxTagLength?: number;
  28 + openModalValidate?: () => boolean;
  29 + primaryKey?: string;
  30 + transformValue?: (list: Recordable[]) => any;
  31 + disabled?: boolean;
  32 + }>(),
  33 + {
  34 + value: () => [],
  35 + maxTagLength: 2,
  36 + primaryKey: 'tbDeviceId',
  37 + }
  38 + );
  39 +
  40 + const emit = defineEmits(['update:value']);
  41 +
  42 + enum Active {
  43 + PENDING = 'pending',
  44 + SELECTED = 'selected',
  45 + }
  46 +
  47 + const activeKey = ref(Active.PENDING);
  48 +
  49 + const { prefixCls } = useDesign('transfer-table-modal');
  50 +
  51 + const pendingTotalList = ref<DeviceModel[]>([]);
  52 +
  53 + const pendingConfirmQueue = ref<DeviceModel[]>([]);
  54 +
  55 + const selectedTotalList = ref<DeviceModel[]>([]);
  56 +
  57 + const selectedConfirmQueue = ref<DeviceModel[]>([]);
  58 +
  59 + const getShowTagOptions = computed(() => {
  60 + const { maxTagLength } = props;
  61 + return unref(selectedTotalList).slice(0, maxTagLength);
  62 + });
  63 +
  64 + const getSurplusOptionsLength = computed(() => {
  65 + const { maxTagLength } = props;
  66 + const surplusValue = unref(selectedTotalList).length - maxTagLength;
  67 + return surplusValue < 0 ? 0 : surplusValue;
  68 + });
  69 +
  70 + // const pendingListCount = computed(() => {
  71 + // const { value } = props;
  72 + // const selectedList = unref(pendingTotalList).filter((item) => value.includes(item.id));
  73 + // return unref(pendingTotalList).length - selectedList.length;
  74 + // });
  75 +
  76 + const [registerModal, { openModal }] = useModal();
  77 +
  78 + const [regsterPendingTable, pendingTableActionType] = useTable({
  79 + ...TransferTableProps,
  80 + rowKey: props.primaryKey,
  81 + api: devicePage,
  82 + immediate: false,
  83 + clickToRowSelect: false,
  84 + beforeFetch: (params) => {
  85 + const { getPendingTableParams } = props;
  86 + const data = getPendingTableParams?.(params) || {};
  87 + Object.assign(params, { ...data, selected: false });
  88 + return params;
  89 + },
  90 + afterFetch: (list: DeviceModel[]) => {
  91 + pendingTotalList.value = list;
  92 + return unref(pendingTotalList);
  93 + },
  94 + rowSelection: {
  95 + type: 'checkbox',
  96 + getCheckboxProps: (record: DeviceModel) => {
  97 + const { primaryKey } = props;
  98 + const checked = unref(selectedTotalList).map((item) => item[primaryKey]);
  99 + return {
  100 + disabled: checked.includes(record[props.primaryKey]!),
  101 + };
  102 + },
  103 + onSelect: (_record: Recordable, _selected: boolean, selectedRows: DeviceModel[]) => {
  104 + const { primaryKey } = props;
  105 + const checked = unref(selectedTotalList).map((item) => item[primaryKey]);
  106 + pendingConfirmQueue.value = selectedRows.filter(
  107 + (item) => !checked.includes(item[primaryKey]!)
  108 + );
  109 + },
  110 + onSelectAll: (_selected: boolean, selectedRows: DeviceModel[]) => {
  111 + const { primaryKey } = props;
  112 + const checked = unref(selectedTotalList).map((item) => item[primaryKey]);
  113 + pendingConfirmQueue.value = selectedRows.filter(
  114 + (item) => !checked.includes(item[primaryKey]!)
  115 + );
  116 + },
  117 + },
  118 + });
  119 +
  120 + const [registerSelectedTable, selectedTableActionType] = useTable({
  121 + formConfig: {
  122 + layout: 'inline',
  123 + labelWidth: 80,
  124 + schemas: deviceTableFormSchema,
  125 + actionColOptions: { span: 6 },
  126 + },
  127 + size: 'small',
  128 + maxHeight: 240,
  129 + useSearchForm: true,
  130 + columns: deviceTableColumn,
  131 + api: async (params) => {
  132 + const { name = '', deviceType = '' } = params || {};
  133 + const items = unref(selectedTotalList).filter((item) => {
  134 + return (
  135 + item.name.toUpperCase().includes(name.toUpperCase()) &&
  136 + item.deviceType.toUpperCase().includes(deviceType.toUpperCase())
  137 + );
  138 + });
  139 + return {
  140 + items,
  141 + total: items.length,
  142 + };
  143 + },
  144 + showIndexColumn: false,
  145 + pagination: { hideOnSinglePage: false },
  146 + fetchSetting: FETCH_SETTING,
  147 + rowKey: props.primaryKey,
  148 + dataSource: selectedTotalList,
  149 + clickToRowSelect: false,
  150 + beforeFetch: (params) => {
  151 + const { getSelectedTableParams } = props;
  152 + const data = getSelectedTableParams?.(params) || {};
  153 + Object.assign(params, { ...data, selected: false });
  154 + return params;
  155 + },
  156 + rowSelection: {
  157 + type: 'checkbox',
  158 + onSelect: (_record: Recordable, _selected: boolean, selectedRows: DeviceModel[]) => {
  159 + selectedConfirmQueue.value = selectedRows;
  160 + },
  161 + onSelectAll: (_selected: boolean, selectedRows: DeviceModel[]) => {
  162 + selectedConfirmQueue.value = selectedRows;
  163 + },
  164 + },
  165 + });
  166 +
  167 + const handleTriggerUpdateValue = () => {
  168 + let list: Recordable[] = cloneDeep(toRaw(unref(selectedTotalList)));
  169 + const { transformValue } = props;
  170 + if (transformValue && isFunction(transformValue)) list = transformValue(list);
  171 +
  172 + emit('update:value', list);
  173 + };
  174 +
  175 + const handleSelected = () => {
  176 + const { primaryKey } = props;
  177 + const _list = [...unref(selectedTotalList), ...unref(pendingConfirmQueue)];
  178 + selectedTotalList.value = uniqBy(_list, primaryKey);
  179 + pendingConfirmQueue.value = [];
  180 +
  181 + handleTriggerUpdateValue();
  182 + pendingTableActionType.setTableData([]);
  183 + nextTick(() => pendingTableActionType.setTableData(unref(pendingTotalList)));
  184 + };
  185 +
  186 + const handleRemoveSelected = () => {
  187 + const { primaryKey } = props;
  188 + const selectedIds = unref(selectedConfirmQueue).map((selected) => selected[primaryKey]);
  189 + remove(unref(selectedTotalList), (item) => selectedIds.includes(item[primaryKey]));
  190 +
  191 + handleTriggerUpdateValue();
  192 +
  193 + selectedTableActionType.clearSelectedRowKeys();
  194 + selectedConfirmQueue.value = [];
  195 + selectedTableActionType.reload();
  196 + };
  197 +
  198 + const handleCheckoutPanel = async () => {
  199 + await nextTick();
  200 + selectedTableActionType.reload();
  201 + };
  202 +
  203 + const handleOpenModal = async () => {
  204 + const { openModalValidate } = props;
  205 +
  206 + if (openModalValidate && isFunction(openModalValidate) && !openModalValidate()) return;
  207 +
  208 + openModal(true);
  209 + await nextTick();
  210 + pendingTableActionType.reload();
  211 + };
  212 +
  213 + onMounted(async () => {
  214 + const { getSelectedTableParams } = props;
  215 + const data = getSelectedTableParams?.({}) || {};
  216 + if (!data?.convertConfigId || !data?.deviceProfileIds) {
  217 + return;
  218 + }
  219 + const { items } = await devicePage({ page: 1, pageSize: 10, ...data, selected: true });
  220 + selectedTotalList.value = items;
  221 + });
  222 +</script>
  223 +
  224 +<template>
  225 + <section>
  226 + <BasicModal
  227 + @register="registerModal"
  228 + title="穿梭表格"
  229 + width="60%"
  230 + :wrapClassName="prefixCls"
  231 + :showOkBtn="false"
  232 + cancelText="关闭"
  233 + >
  234 + <section class="bg-gray-100">
  235 + <Tabs v-model:active-key="activeKey" type="card" @change="handleCheckoutPanel">
  236 + <Tabs.TabPane :key="Active.PENDING">
  237 + <template #tab>
  238 + <div class="flex items-center justify-center">
  239 + <span>待选设备</span>
  240 + <!-- <Badge show-zero :count="pendingListCount" /> -->
  241 + </div>
  242 + </template>
  243 + <BasicTable @register="regsterPendingTable">
  244 + <template #toolbar>
  245 + <section class="flex w-full justify-end items-center">
  246 + <!-- <Button type="primary">全选</Button> -->
  247 + <div class="text-blue-400">
  248 + <span class="mr-2">选择设备:</span>
  249 + <span>{{ pendingConfirmQueue.length }}</span>
  250 + </div>
  251 + </section>
  252 + </template>
  253 + </BasicTable>
  254 + <section class="flex justify-end px-4 pb-4">
  255 + <Button
  256 + type="primary"
  257 + @click="handleSelected"
  258 + :disabled="!pendingConfirmQueue.length"
  259 + >
  260 + <span>确定已选</span>
  261 + </Button>
  262 + </section>
  263 + </Tabs.TabPane>
  264 + <Tabs.TabPane :key="Active.SELECTED">
  265 + <template #tab>
  266 + <div class="flex items-center justify-center">
  267 + <span>已选设备</span>
  268 + <!-- <Badge show-zero :count="selectedTotalList.length" /> -->
  269 + </div>
  270 + </template>
  271 + <BasicTable @register="registerSelectedTable">
  272 + <template #toolbar>
  273 + <section class="flex w-full justify-end items-center">
  274 + <div class="text-blue-400">
  275 + <span class="mr-2">选择设备:</span>
  276 + <span>{{ selectedConfirmQueue.length }}</span>
  277 + </div>
  278 + </section>
  279 + </template>
  280 + </BasicTable>
  281 + <section class="flex justify-end px-4 pb-4">
  282 + <Button
  283 + type="primary"
  284 + :disabled="!selectedConfirmQueue.length"
  285 + @click="handleRemoveSelected"
  286 + >
  287 + <span>移除已选</span>
  288 + </Button>
  289 + </section>
  290 + </Tabs.TabPane>
  291 + </Tabs>
  292 + </section>
  293 + </BasicModal>
  294 + <Button @click="handleOpenModal" type="link" :disabled="disabled">
  295 + <span v-if="!selectedTotalList.length">选择设备</span>
  296 + <div v-if="selectedTotalList.length">
  297 + <Tag
  298 + class="!px-2 !py-1 !bg-gray-50 !border-gray-100"
  299 + v-for="item in getShowTagOptions"
  300 + :key="item[primaryKey]"
  301 + >
  302 + <span>
  303 + {{ item.alias || item.name }}
  304 + </span>
  305 + </Tag>
  306 + <Tag class="!px-2 !py-1 !bg-gray-50 !border-gray-100" v-if="getSurplusOptionsLength">
  307 + <span> +{{ getSurplusOptionsLength }}... </span>
  308 + </Tag>
  309 + </div>
  310 + </Button>
  311 + </section>
  312 +</template>
  313 +
  314 +<style lang="less">
  315 + @prefix-cls: ~'@{namespace}-transfer-table-modal';
  316 +
  317 + .@{prefix-cls} {
  318 + .vben-basic-table {
  319 + padding-top: 0;
  320 + }
  321 +
  322 + .vben-basic-form > .ant-row {
  323 + width: 100%;
  324 + }
  325 +
  326 + .ant-tabs-top-bar {
  327 + background-color: #fff;
  328 + }
  329 +
  330 + .transfer-table-disabled-row {
  331 + :deep(.ant-checkbox) {
  332 + cursor: not-allowed;
  333 +
  334 + .ant-checkbox-inner {
  335 + background-color: #f5f5f5;
  336 + border-color: #d9d9d9 !important;
  337 + }
  338 + }
  339 + }
  340 + }
  341 +</style>
1 import { FormSchema } from '/@/components/Form'; 1 import { FormSchema } from '/@/components/Form';
2 import { findDictItemByCode } from '/@/api/system/dict'; 2 import { findDictItemByCode } from '/@/api/system/dict';
3 -import { h, unref } from 'vue';  
4 import { getDeviceProfile } from '/@/api/alarm/position'; 3 import { getDeviceProfile } from '/@/api/alarm/position';
5 -import { BasicColumn, BasicTableProps } from '/@/components/Table';  
6 -import { devicePage } from '/@/api/device/deviceManager';  
7 -import { Tag } from 'ant-design-vue'; 4 +import { BasicInfoFormField, DataSourceType } from '../enum';
8 import { DeviceRecord } from '/@/api/device/model/deviceModel'; 5 import { DeviceRecord } from '/@/api/device/model/deviceModel';
9 -import { FETCH_SETTING } from '/@/components/Table/src/const';  
10 -import { useMessage } from '/@/hooks/web/useMessage';  
11 -import {  
12 - BasicInfoFormField,  
13 - DataSourceType,  
14 - DeviceStatusEnum,  
15 - DeviceStatusNameEnum,  
16 - DeviceTypeNameEnum,  
17 -} from '../enum';  
18 -import { useClipboard } from '@vueuse/core';  
19 6
20 export const stepConfig = ['选择流转方式', '完善配置参数']; 7 export const stepConfig = ['选择流转方式', '完善配置参数'];
21 8
22 export const removeFieldByModeForm = ['name', 'description']; 9 export const removeFieldByModeForm = ['name', 'description'];
23 10
24 -//表单通用配置  
25 -export const modelFormPublicConfig = {  
26 - labelWidth: 120,  
27 - actionColOptions: {  
28 - span: 14,  
29 - },  
30 - showResetButton: false,  
31 - showSubmitButton: false,  
32 -};  
33 -  
34 const handleGroupDevice = (options: DeviceRecord[]) => { 11 const handleGroupDevice = (options: DeviceRecord[]) => {
35 const map = new Map<string, string[]>(); 12 const map = new Map<string, string[]>();
36 options.forEach((item) => { 13 options.forEach((item) => {
@@ -46,114 +23,17 @@ const handleGroupDevice = (options: DeviceRecord[]) => { @@ -46,114 +23,17 @@ const handleGroupDevice = (options: DeviceRecord[]) => {
46 return value; 23 return value;
47 }; 24 };
48 25
49 -const deviceTableFormSchema: FormSchema[] = [  
50 - {  
51 - field: 'name',  
52 - label: '设备名称',  
53 - component: 'Input',  
54 - colProps: { span: 9 },  
55 - componentProps: {  
56 - placeholder: '请输入设备名称',  
57 - },  
58 - },  
59 - {  
60 - field: 'deviceType',  
61 - label: '设备类型',  
62 - component: 'ApiSelect',  
63 - colProps: { span: 9 },  
64 - componentProps: {  
65 - placeholder: '请选择设备类型',  
66 - api: findDictItemByCode,  
67 - params: {  
68 - dictCode: 'device_type',  
69 - },  
70 - labelField: 'itemText',  
71 - valueField: 'itemValue',  
72 - },  
73 - },  
74 -];  
75 -  
76 -const { copied, copy } = useClipboard({ legacy: true });  
77 -  
78 -const { createMessage } = useMessage();  
79 -  
80 -const deviceTableColumn: BasicColumn[] = [  
81 - {  
82 - title: '状态',  
83 - dataIndex: 'deviceState',  
84 - customRender: ({ text }) => {  
85 - return h(  
86 - Tag,  
87 - {  
88 - color:  
89 - text === DeviceStatusEnum.INACTIVE  
90 - ? 'warning'  
91 - : text === DeviceStatusEnum.OFFLINE  
92 - ? 'error'  
93 - : 'success',  
94 - },  
95 - () => DeviceStatusNameEnum[text]  
96 - );  
97 - },  
98 - },  
99 - {  
100 - title: '别名/设备名称',  
101 - dataIndex: 'name',  
102 - customRender: ({ record }) => {  
103 - return h('div', [  
104 - h(  
105 - 'div',  
106 - {  
107 - class: 'cursor-pointer',  
108 - onClick: async () => {  
109 - await copy(record.name);  
110 - if (unref(copied)) createMessage.success('复制成功~');  
111 - },  
112 - },  
113 - [  
114 - record.alias && h('div', { class: 'truncate' }, record.alias),  
115 - h('div', { class: 'text-blue-400 truncate' }, record.name),  
116 - ]  
117 - ),  
118 - ]);  
119 - },  
120 - },  
121 - {  
122 - title: '设备类型',  
123 - dataIndex: 'deviceType',  
124 - customRender: ({ text }) => {  
125 - return h(Tag, { color: 'success' }, () => DeviceTypeNameEnum[text]);  
126 - },  
127 - },  
128 - {  
129 - title: '所属产品',  
130 - dataIndex: 'deviceProfile.name',  
131 - },  
132 - {  
133 - title: '所属组织',  
134 - dataIndex: 'organizationDTO.name',  
135 - },  
136 -];  
137 -  
138 -const TransferTableProps: BasicTableProps = {  
139 - formConfig: {  
140 - layout: 'inline',  
141 - labelWidth: 80,  
142 - schemas: deviceTableFormSchema,  
143 - actionColOptions: { span: 6 }, 26 +//表单通用配置
  27 +export const modelFormPublicConfig = {
  28 + labelWidth: 120,
  29 + actionColOptions: {
  30 + span: 14,
144 }, 31 },
145 - size: 'small',  
146 - maxHeight: 240,  
147 - useSearchForm: true,  
148 - columns: deviceTableColumn,  
149 - showIndexColumn: false,  
150 - fetchSetting: FETCH_SETTING,  
151 -} as BasicTableProps; 32 + showResetButton: false,
  33 + showSubmitButton: false,
  34 +};
152 35
153 -export const modeForm = (  
154 - submitFn: Function | undefined,  
155 - disabled?: boolean | any  
156 -): FormSchema[] => { 36 +export const modeForm = (disabled: boolean): FormSchema[] => {
157 return [ 37 return [
158 { 38 {
159 field: BasicInfoFormField.CONVERT_CONFIG_ID, 39 field: BasicInfoFormField.CONVERT_CONFIG_ID,
@@ -218,70 +98,21 @@ export const modeForm = ( @@ -218,70 +98,21 @@ export const modeForm = (
218 rules: [{ required: true, message: '数据源设备为必选项', type: 'array' }], 98 rules: [{ required: true, message: '数据源设备为必选项', type: 'array' }],
219 componentProps: ({ formActionType }) => { 99 componentProps: ({ formActionType }) => {
220 const { getFieldsValue } = formActionType; 100 const { getFieldsValue } = formActionType;
221 - const values = getFieldsValue();  
222 - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);  
223 - const devices = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_DEVICE);  
224 -  
225 return { 101 return {
226 - labelField: 'name',  
227 - valueField: 'tbDeviceId',  
228 - primaryKey: 'tbDeviceId',  
229 disabled, 102 disabled,
230 - pendingTableProps: {  
231 - ...TransferTableProps,  
232 - api: devicePage,  
233 - beforeFetch: (params) => {  
234 - const values = getFieldsValue();  
235 - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);  
236 - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);  
237 - if (convertConfigId) {  
238 - Object.assign(params, { convertConfigId, selected: false });  
239 - }  
240 - return { ...params, deviceProfileIds };  
241 - },  
242 - } as BasicTableProps,  
243 - selectedTableProps: {  
244 - ...TransferTableProps,  
245 - // api  
246 - api: !!(convertConfigId && devices) ? devicePage : undefined,  
247 - beforeFetch: (params) => {  
248 - const values = getFieldsValue();  
249 - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);  
250 - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);  
251 - if (convertConfigId) {  
252 - Object.assign(params, { convertConfigId, selected: true });  
253 - }  
254 - return { ...params, deviceProfileIds };  
255 - },  
256 - } as BasicTableProps,  
257 - initSelectedOptions: async ({ setSelectedTotal }) => { 103 + getPendingTableParams: () => {
258 const values = getFieldsValue(); 104 const values = getFieldsValue();
259 const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); 105 const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);
260 const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); 106 const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);
261 - const devices = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_DEVICE);  
262 - if (convertConfigId && devices) {  
263 - const { items, total } = await devicePage({  
264 - page: 1,  
265 - pageSize: 10,  
266 - convertConfigId: values[BasicInfoFormField.CONVERT_CONFIG_ID],  
267 - deviceProfileIds,  
268 - selected: true,  
269 - });  
270 - setSelectedTotal(total);  
271 - return items;  
272 - }  
273 - return []; 107 + return { convertConfigId, deviceProfileIds };
274 }, 108 },
275 - onSelectedAfter: async () => {  
276 - submitFn && (await submitFn(false));  
277 - },  
278 - onRemoveAfter: async ({ reloadSelected }) => {  
279 - submitFn && (await submitFn(false));  
280 - reloadSelected();  
281 - },  
282 - transformValue: (_selectedRowKeys: string[], selectedRows: DeviceRecord[]) => {  
283 - return handleGroupDevice(selectedRows); 109 + getSelectedTableParams: () => {
  110 + const values = getFieldsValue();
  111 + const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);
  112 + const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);
  113 + return { convertConfigId, deviceProfileIds };
284 }, 114 },
  115 + transformValue: handleGroupDevice,
285 }; 116 };
286 }, 117 },
287 }, 118 },
  1 +import { FormSchema } from '/@/components/Form';
  2 +import { findDictItemByCode } from '/@/api/system/dict';
  3 +import { ref } from 'vue';
  4 +import { isExistDataManagerNameApi } from '/@/api/datamanager/dataManagerApi';
  5 +import { getDeviceProfile } from '/@/api/alarm/position';
  6 +import { DeviceRecord } from '/@/api/device/model/deviceModel';
  7 +
  8 +const typeValue = ref('');
  9 +export enum CredentialsEnum {
  10 + IS_ANONYMOUS = 'anonymous',
  11 + IS_BASIC = 'basic',
  12 + IS_PEM = 'pem',
  13 +}
  14 +export const isBasic = (type: string) => {
  15 + return type === CredentialsEnum.IS_BASIC;
  16 +};
  17 +export const isPem = (type: string) => {
  18 + return type === CredentialsEnum.IS_PEM;
  19 +};
  20 +
  21 +export enum DataSourceType {
  22 + ALL = 'ALL',
  23 + PRODUCT = 'PRODUCTS',
  24 + DEVICE = 'DEVICES',
  25 +}
  26 +
  27 +export enum BasicInfoFormField {
  28 + DATA_SOURCE_TYPE = 'datasourceType',
  29 + DATA_SOURCE_PRODUCT = 'datasourceProduct',
  30 + DATA_SOURCE_DEVICE = 'datasourceDevice',
  31 + CONVERT_CONFIG_ID = 'convertConfigId',
  32 +}
  33 +
  34 +export enum DeviceStatusEnum {
  35 + OFFLINE = 'OFFLINE',
  36 + ONLINE = 'ONLINE',
  37 + INACTIVE = 'INACTIVE',
  38 +}
  39 +
  40 +export enum DeviceStatusNameEnum {
  41 + OFFLINE = '离线',
  42 + ONLINE = '在线',
  43 + INACTIVE = '待激活',
  44 +}
  45 +
  46 +export enum DeviceTypeEnum {
  47 + SENSOR = 'SENSOR',
  48 + DIRECT_CONNECTION = 'DIRECT_CONNECTION',
  49 + GATEWAY = 'GATEWAY',
  50 +}
  51 +
  52 +export enum DeviceTypeNameEnum {
  53 + SENSOR = '网关子设备',
  54 + DIRECT_CONNECTION = '直连设备',
  55 + GATEWAY = '网关设备',
  56 +}
  57 +
  58 +const handleGroupDevice = (options: DeviceRecord[]) => {
  59 + const map = new Map<string, string[]>();
  60 + options.forEach((item) => {
  61 + if (map.has(item.profileId)) {
  62 + const deviceList = map.get(item.profileId)!;
  63 + deviceList.push(item.tbDeviceId);
  64 + } else {
  65 + map.set(item.profileId, [item.tbDeviceId]);
  66 + }
  67 + });
  68 + const value = Array.from(map.entries()).map(([product, devices]) => ({ product, devices }));
  69 +
  70 + return value;
  71 +};
  72 +
  73 +export const modeForm = (): FormSchema[] => {
  74 + return [
  75 + {
  76 + field: BasicInfoFormField.CONVERT_CONFIG_ID,
  77 + label: '',
  78 + component: 'Input',
  79 + show: false,
  80 + },
  81 + {
  82 + field: BasicInfoFormField.DATA_SOURCE_TYPE,
  83 + label: '数据源',
  84 + component: 'RadioGroup',
  85 + defaultValue: DataSourceType.ALL,
  86 + componentProps: {
  87 + options: [
  88 + { label: '全部', value: DataSourceType.ALL },
  89 + { label: '产品', value: DataSourceType.PRODUCT },
  90 + { label: '设备', value: DataSourceType.DEVICE },
  91 + ],
  92 + },
  93 + },
  94 + {
  95 + field: BasicInfoFormField.DATA_SOURCE_PRODUCT,
  96 + label: '数据源产品',
  97 + component: 'TransferModal',
  98 + ifShow: ({ model }) => {
  99 + return model[BasicInfoFormField.DATA_SOURCE_TYPE] !== DataSourceType.ALL;
  100 + },
  101 + valueField: 'value',
  102 + changeEvent: 'update:value',
  103 + componentProps: ({ formActionType }) => {
  104 + const { setFieldsValue } = formActionType;
  105 + return {
  106 + api: getDeviceProfile,
  107 + labelField: 'name',
  108 + valueField: 'tbProfileId',
  109 + transferProps: {
  110 + listStyle: { height: '400px' },
  111 + showSearch: true,
  112 + filterOption: (inputValue: string, option: Recordable) => {
  113 + const upperCaseInputValue = inputValue.toUpperCase();
  114 + const upperCaseOptionValue = option.name.toUpperCase();
  115 + return upperCaseOptionValue.includes(upperCaseInputValue);
  116 + },
  117 + },
  118 + onChange: () => {
  119 + setFieldsValue({ [BasicInfoFormField.DATA_SOURCE_DEVICE]: [] });
  120 + },
  121 + };
  122 + },
  123 + },
  124 + {
  125 + field: BasicInfoFormField.DATA_SOURCE_DEVICE,
  126 + label: '数据源设备',
  127 + component: 'TransferTableModal',
  128 + ifShow: ({ model }) => {
  129 + return model[BasicInfoFormField.DATA_SOURCE_TYPE] === DataSourceType.DEVICE;
  130 + },
  131 + valueField: 'value',
  132 + changeEvent: 'update:value',
  133 + componentProps: ({ formActionType }) => {
  134 + const { getFieldsValue } = formActionType;
  135 + return {
  136 + getPendingTableParams: () => {
  137 + const values = getFieldsValue();
  138 + const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);
  139 + const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);
  140 + return { convertConfigId, deviceProfileIds };
  141 + },
  142 + getSelectedTableParams: () => {
  143 + const values = getFieldsValue();
  144 + const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);
  145 + const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);
  146 + return { convertConfigId, deviceProfileIds };
  147 + },
  148 + transformValue: handleGroupDevice,
  149 + };
  150 + },
  151 + },
  152 + {
  153 + field: 'type',
  154 + label: '转换方式',
  155 + component: 'ApiSelect',
  156 + required: true,
  157 + colProps: {
  158 + span: 24,
  159 + },
  160 + componentProps({}) {
  161 + return {
  162 + api: findDictItemByCode,
  163 + params: {
  164 + dictCode: 'convert_data_to',
  165 + },
  166 + labelField: 'itemText',
  167 + valueField: 'itemValue',
  168 + onChange(value) {
  169 + typeValue.value = value;
  170 + },
  171 + };
  172 + },
  173 + },
  174 + {
  175 + field: 'remark',
  176 + label: '描述',
  177 + colProps: { span: 24 },
  178 + component: 'Input',
  179 + componentProps: {
  180 + maxLength: 255,
  181 + placeholder: '请输入描述',
  182 + },
  183 + },
  184 + ];
  185 +};
  186 +
  187 +export const modeKafkaForm: FormSchema[] = [
  188 + {
  189 + field: 'name',
  190 + label: '名称',
  191 + colProps: { span: 12 },
  192 + required: true,
  193 + component: 'Input',
  194 + componentProps: {
  195 + maxLength: 255,
  196 + placeholder: '请输入名称',
  197 + },
  198 + dynamicRules: () => {
  199 + return [
  200 + {
  201 + required: true,
  202 + validator(_, value) {
  203 + return new Promise((resolve, reject) => {
  204 + if (value == '') {
  205 + reject('请输入名称');
  206 + } else {
  207 + resolve();
  208 + }
  209 + });
  210 + },
  211 + },
  212 + ];
  213 + },
  214 + },
  215 + {
  216 + field: 'topicPattern',
  217 + label: '消息主题',
  218 + colProps: { span: 12 },
  219 + required: true,
  220 + component: 'Input',
  221 + defaultValue: 'my-topic',
  222 + componentProps: {
  223 + maxLength: 255,
  224 + placeholder: '请输入消息主题',
  225 + },
  226 + },
  227 + {
  228 + field: 'bootstrapServers',
  229 + label: '服务器',
  230 + colProps: { span: 12 },
  231 + component: 'Input',
  232 + defaultValue: 'localhost:9092',
  233 + required: true,
  234 + componentProps: {
  235 + maxLength: 255,
  236 + placeholder: 'localhost:9092',
  237 + },
  238 + },
  239 + {
  240 + field: 'retries',
  241 + label: '重连次数',
  242 + colProps: { span: 12 },
  243 + component: 'InputNumber',
  244 + defaultValue: 0,
  245 + componentProps: {
  246 + maxLength: 255,
  247 + },
  248 + },
  249 + {
  250 + field: 'batchSize',
  251 + label: '生产者并发',
  252 + colProps: { span: 12 },
  253 + component: 'InputNumber',
  254 + defaultValue: 16384,
  255 + componentProps: {
  256 + maxLength: 255,
  257 + },
  258 + },
  259 + {
  260 + field: 'linger',
  261 + label: '缓存时间',
  262 + colProps: { span: 12 },
  263 + component: 'InputNumber',
  264 + defaultValue: 0,
  265 + componentProps: {
  266 + maxLength: 255,
  267 + },
  268 + },
  269 + {
  270 + field: 'bufferMemory',
  271 + label: '最大缓存',
  272 + colProps: { span: 12 },
  273 + component: 'InputNumber',
  274 + defaultValue: 33554432,
  275 + componentProps: {
  276 + maxLength: 255,
  277 + },
  278 + },
  279 + {
  280 + field: 'acks',
  281 + component: 'Select',
  282 + label: '响应码',
  283 + colProps: { span: 12 },
  284 + defaultValue: '-1',
  285 + componentProps: {
  286 + placeholder: '请选择响应码',
  287 + options: [
  288 + { label: 'all', value: 'all' },
  289 + { label: '-1', value: '-1' },
  290 + { label: '0', value: '0' },
  291 + { label: '1', value: '1' },
  292 + ],
  293 + },
  294 + },
  295 + {
  296 + field: 'keySerializer',
  297 + label: '键序列化',
  298 + colProps: { span: 24 },
  299 + required: true,
  300 + component: 'Input',
  301 + defaultValue: 'org.apache.kafka.common.serialization.StringSerializer',
  302 + componentProps: {
  303 + maxLength: 255,
  304 + placeholder: 'org.apache.kafka.common.serialization.StringSerializer',
  305 + },
  306 + },
  307 + {
  308 + field: 'valueSerializer',
  309 + label: '值序列化',
  310 + colProps: { span: 24 },
  311 + required: true,
  312 + component: 'Input',
  313 + defaultValue: 'org.apache.kafka.common.serialization.StringSerializer',
  314 + componentProps: {
  315 + maxLength: 255,
  316 + placeholder: 'org.apache.kafka.common.serialization.StringSerializer',
  317 + },
  318 + },
  319 + {
  320 + field: 'otherProperties',
  321 + label: '其他属性',
  322 + colProps: { span: 24 },
  323 + component: 'JAddInput',
  324 + subLabel: '不可重复',
  325 + },
  326 + {
  327 + field: 'addMetadataKeyValuesAsKafkaHeaders',
  328 + label: '是否启用',
  329 + colProps: { span: 12 },
  330 + component: 'Checkbox',
  331 + renderComponentContent: '将消息的元数据以键值对的方式添加到Kafka消息头中',
  332 + },
  333 + {
  334 + field: 'kafkaHeadersCharset',
  335 + component: 'Select',
  336 + label: '字符集',
  337 + required: true,
  338 + colProps: { span: 12 },
  339 + defaultValue: 'UTF-8',
  340 + componentProps: {
  341 + placeholder: '请选择字符集编码',
  342 + options: [
  343 + { label: 'US-ASCII', value: 'US' },
  344 + { label: 'ISO-8859-1', value: 'ISO-8859-1' },
  345 + { label: 'UTF-8', value: 'UTF-8' },
  346 + { label: 'UTF-16BE', value: 'UTF-16BE' },
  347 + { label: 'UTF-16LE', value: 'UTF-16LE' },
  348 + { label: 'UTF-16', value: 'UTF-16' },
  349 + ],
  350 + },
  351 + ifShow: ({ values }) => {
  352 + return !!values.addMetadataKeyValuesAsKafkaHeaders;
  353 + },
  354 + },
  355 + {
  356 + field: 'description',
  357 + label: '说明',
  358 + colProps: { span: 24 },
  359 + component: 'InputTextArea',
  360 + componentProps: {
  361 + maxLength: 255,
  362 + rows: 4,
  363 + placeholder: '请输入说明',
  364 + },
  365 + },
  366 +];
  367 +
  368 +export const modeMqttForm: FormSchema[] = [
  369 + {
  370 + field: 'name',
  371 + label: '名称',
  372 + colProps: { span: 12 },
  373 + component: 'Input',
  374 + componentProps: {
  375 + maxLength: 255,
  376 + placeholder: '请输入名称',
  377 + },
  378 + },
  379 + {
  380 + field: 'topicPattern',
  381 + label: '主题模式',
  382 + colProps: { span: 12 },
  383 + required: true,
  384 + component: 'Input',
  385 + defaultValue: 'my-topic',
  386 + componentProps: {
  387 + maxLength: 255,
  388 + placeholder: '请输入Topic pattern',
  389 + },
  390 + },
  391 + {
  392 + field: 'host',
  393 + label: '主机',
  394 + colProps: { span: 12 },
  395 + component: 'Input',
  396 + componentProps: {
  397 + maxLength: 255,
  398 + placeholder: '请输入Host',
  399 + },
  400 + },
  401 + {
  402 + field: 'port',
  403 + label: '端口',
  404 + colProps: { span: 12 },
  405 + component: 'InputNumber',
  406 + defaultValue: 1883,
  407 + required: true,
  408 + componentProps: {
  409 + maxLength: 255,
  410 + placeholder: '请输入Port',
  411 + },
  412 + },
  413 + {
  414 + field: 'connectTimeoutSec',
  415 + label: '连接超时(秒)',
  416 + colProps: { span: 12 },
  417 + component: 'InputNumber',
  418 + defaultValue: 10,
  419 + required: true,
  420 + componentProps: {
  421 + maxLength: 255,
  422 + placeholder: '请输入Connection timeout (sec)',
  423 + },
  424 + },
  425 + {
  426 + field: 'clientId',
  427 + label: '客户端ID',
  428 + colProps: { span: 12 },
  429 + component: 'Input',
  430 + componentProps: ({ formActionType }) => {
  431 + const { updateSchema } = formActionType;
  432 + return {
  433 + onChange(e) {
  434 + if (!e.data) {
  435 + updateSchema({
  436 + field: 'appendClientIdSuffix',
  437 + show: false,
  438 + });
  439 + } else {
  440 + updateSchema({
  441 + field: 'appendClientIdSuffix',
  442 + show: true,
  443 + });
  444 + }
  445 + },
  446 + maxLength: 255,
  447 + placeholder: '请输入Client ID',
  448 + };
  449 + },
  450 + },
  451 + {
  452 + field: 'appendClientIdSuffix',
  453 + label: '',
  454 + colProps: { span: 12 },
  455 + defaultValue: false,
  456 + component: 'Checkbox',
  457 + renderComponentContent: '将服务ID作为后缀添加到客户端ID',
  458 + show: false,
  459 + },
  460 + {
  461 + field: 'cleanSession',
  462 + label: '是否启用',
  463 + colProps: { span: 12 },
  464 + defaultValue: true,
  465 + component: 'Checkbox',
  466 + renderComponentContent: '清除会话',
  467 + },
  468 + {
  469 + field: 'ssl',
  470 + label: '是否启用',
  471 + colProps: { span: 12 },
  472 + defaultValue: false,
  473 + component: 'Checkbox',
  474 + renderComponentContent: '启用SSL',
  475 + },
  476 + {
  477 + field: 'type',
  478 + component: 'Select',
  479 + label: '凭据类型',
  480 + colProps: { span: 12 },
  481 + defaultValue: 'anonymous',
  482 + componentProps: {
  483 + placeholder: '请选择Credentials',
  484 + options: [
  485 + { label: 'Anonymous', value: 'anonymous' },
  486 + { label: 'Basic', value: 'basic' },
  487 + { label: 'PEM', value: 'pem' },
  488 + ],
  489 + },
  490 + },
  491 + {
  492 + field: 'username',
  493 + label: '用户名',
  494 + colProps: { span: 12 },
  495 + component: 'Input',
  496 + required: true,
  497 + componentProps: {
  498 + maxLength: 255,
  499 + placeholder: '请输入用户名',
  500 + },
  501 + ifShow: ({ values }) => isBasic(Reflect.get(values, 'type')),
  502 + },
  503 + {
  504 + field: 'password',
  505 + label: '密码',
  506 + colProps: { span: 12 },
  507 + component: 'InputPassword',
  508 + componentProps: {
  509 + maxLength: 255,
  510 + placeholder: '请输入密码',
  511 + },
  512 + ifShow: ({ values }) => isBasic(Reflect.get(values, 'type')),
  513 + },
  514 + {
  515 + field: '4',
  516 + label: '',
  517 + colProps: { span: 24 },
  518 + component: 'Input',
  519 + slot: 'uploadAdd1',
  520 + ifShow: ({ values }) => isPem(Reflect.get(values, 'type')),
  521 + },
  522 + {
  523 + field: '11',
  524 + label: '',
  525 + colProps: { span: 24 },
  526 + component: 'Input',
  527 + slot: 'showImg1',
  528 + ifShow: ({ values }) => isPem(Reflect.get(values, 'type')),
  529 + },
  530 + {
  531 + field: '5',
  532 + label: '',
  533 + colProps: { span: 24 },
  534 + component: 'Input',
  535 + slot: 'uploadAdd2',
  536 + ifShow: ({ values }) => isPem(Reflect.get(values, 'type')),
  537 + },
  538 + {
  539 + field: '1111',
  540 + label: '',
  541 + colProps: { span: 24 },
  542 + component: 'Input',
  543 + slot: 'showImg2',
  544 + ifShow: ({ values }) => isPem(Reflect.get(values, 'type')),
  545 + },
  546 + {
  547 + field: '6',
  548 + label: '',
  549 + colProps: { span: 24 },
  550 + component: 'Input',
  551 + slot: 'uploadAdd3',
  552 + ifShow: ({ values }) => isPem(Reflect.get(values, 'type')),
  553 + },
  554 + {
  555 + field: '111111',
  556 + label: '',
  557 + colProps: { span: 24 },
  558 + component: 'Input',
  559 + slot: 'showImg3',
  560 + ifShow: ({ values }) => isPem(Reflect.get(values, 'type')),
  561 + },
  562 + {
  563 + field: 'password',
  564 + label: '密码',
  565 + colProps: { span: 12 },
  566 + component: 'InputPassword',
  567 + componentProps: {
  568 + maxLength: 255,
  569 + placeholder: '请输入密码',
  570 + },
  571 + ifShow: ({ values }) => isPem(Reflect.get(values, 'type')),
  572 + },
  573 + {
  574 + field: 'description',
  575 + label: '说明',
  576 + colProps: { span: 24 },
  577 + component: 'InputTextArea',
  578 + componentProps: {
  579 + maxLength: 255,
  580 + rows: 4,
  581 + placeholder: '请输入说明',
  582 + },
  583 + },
  584 +];
  585 +
  586 +export const modeRabbitMqForm: FormSchema[] = [
  587 + {
  588 + field: 'name',
  589 + label: '名称',
  590 + colProps: { span: 12 },
  591 + required: true,
  592 + component: 'Input',
  593 + componentProps: {
  594 + maxLength: 255,
  595 + placeholder: '请输入名称',
  596 + },
  597 + dynamicRules: () => {
  598 + return [
  599 + {
  600 + required: true,
  601 + validator(_, value) {
  602 + return new Promise((resolve, reject) => {
  603 + if (value == '') {
  604 + reject('请输入名称');
  605 + } else {
  606 + resolve();
  607 + }
  608 + });
  609 + },
  610 + },
  611 + ];
  612 + },
  613 + },
  614 + {
  615 + field: 'exchangeNamePattern',
  616 + label: '交换名称模式',
  617 + colProps: { span: 12 },
  618 + component: 'Input',
  619 + componentProps: {
  620 + maxLength: 255,
  621 + placeholder: '请输入模式',
  622 + },
  623 + },
  624 + {
  625 + field: 'routingKeyPattern',
  626 + label: '路由密钥模式',
  627 + colProps: { span: 12 },
  628 + component: 'Input',
  629 + componentProps: {
  630 + maxLength: 255,
  631 + placeholder: '请输入模式',
  632 + },
  633 + },
  634 + {
  635 + field: 'messageProperties',
  636 + component: 'Select',
  637 + label: '消息属性',
  638 + colProps: { span: 12 },
  639 + componentProps: {
  640 + placeholder: '请选择消息属性',
  641 + options: [
  642 + { label: 'BASIC', value: 'BASIC' },
  643 + { label: 'TEXT_PLAIN', value: 'TEXT_PLAIN' },
  644 + { label: 'MINIMAL_BASIC', value: 'MINIMAL_BASIC' },
  645 + { label: 'MINIMAL_PERSISTENT_BASIC', value: 'MINIMAL_PERSISTENT_BASIC' },
  646 + { label: 'PERSISTENT_BASIC', value: 'PERSISTENT_BASIC' },
  647 + { label: 'PERSISTENT_TEXT_PLAIN', value: 'PERSISTENT_TEXT_PLAIN' },
  648 + ],
  649 + },
  650 + },
  651 + {
  652 + field: 'host',
  653 + label: '主机',
  654 + colProps: { span: 12 },
  655 + component: 'Input',
  656 + required: true,
  657 + defaultValue: 'localhost',
  658 + componentProps: {
  659 + maxLength: 255,
  660 + placeholder: 'localhost',
  661 + },
  662 + },
  663 + {
  664 + field: 'port',
  665 + label: '端口',
  666 + colProps: { span: 12 },
  667 + component: 'InputNumber',
  668 + defaultValue: 5672,
  669 + required: true,
  670 + componentProps: {
  671 + maxLength: 255,
  672 + placeholder: '请输入Port',
  673 + },
  674 + },
  675 + {
  676 + field: 'virtualHost',
  677 + label: '虚拟端口(以/开头)',
  678 + colProps: { span: 12 },
  679 + component: 'Input',
  680 + defaultValue: '/',
  681 + componentProps: {
  682 + maxLength: 255,
  683 + placeholder: '/',
  684 + },
  685 + },
  686 + {
  687 + field: 'username',
  688 + label: '用户名',
  689 + colProps: { span: 12 },
  690 + component: 'Input',
  691 + defaultValue: 'guest',
  692 + componentProps: {
  693 + maxLength: 255,
  694 + placeholder: '请输入用户名',
  695 + },
  696 + },
  697 + {
  698 + field: 'password',
  699 + label: '密码',
  700 + colProps: { span: 12 },
  701 + component: 'InputPassword',
  702 + defaultValue: 'guest',
  703 + componentProps: {
  704 + maxLength: 255,
  705 + placeholder: '请输入密码',
  706 + },
  707 + },
  708 + {
  709 + field: 'automaticRecoveryEnabled',
  710 + label: '是否启用',
  711 + colProps: { span: 12 },
  712 + component: 'Checkbox',
  713 + renderComponentContent: '自动恢复',
  714 + },
  715 + {
  716 + field: 'connectionTimeout',
  717 + label: '连接超时(毫秒)',
  718 + colProps: { span: 12 },
  719 + component: 'InputNumber',
  720 + defaultValue: 60000,
  721 + componentProps: {
  722 + maxLength: 255,
  723 + placeholder: '请输入Connection timeout (ms)',
  724 + },
  725 + },
  726 + {
  727 + field: 'handshakeTimeout',
  728 + label: '握手超时(毫秒)',
  729 + colProps: { span: 12 },
  730 + component: 'InputNumber',
  731 + defaultValue: 10000,
  732 + componentProps: {
  733 + maxLength: 255,
  734 + placeholder: '请输入Handshake timeout (ms)',
  735 + },
  736 + },
  737 + {
  738 + field: 'clientProperties',
  739 + label: '客户端属性',
  740 + colProps: { span: 24 },
  741 + component: 'JAddInput',
  742 + subLabel: '不可重复',
  743 + },
  744 + {
  745 + field: 'description',
  746 + label: '说明',
  747 + colProps: { span: 24 },
  748 + component: 'InputTextArea',
  749 + componentProps: {
  750 + maxLength: 255,
  751 + rows: 4,
  752 + placeholder: '请输入说明',
  753 + },
  754 + },
  755 +];
  756 +
  757 +export const modeApiForm: FormSchema[] = [
  758 + {
  759 + field: 'name',
  760 + label: '名称',
  761 + colProps: { span: 12 },
  762 + required: true,
  763 + component: 'Input',
  764 + componentProps: {
  765 + maxLength: 255,
  766 + placeholder: '请输入名称',
  767 + },
  768 + dynamicRules: ({ values }) => {
  769 + return [
  770 + {
  771 + required: true,
  772 + validator(_, value) {
  773 + return new Promise((resolve, reject) => {
  774 + if (value == '') {
  775 + reject('请输入名称');
  776 + } else {
  777 + if (values.name) {
  778 + isExistDataManagerNameApi({
  779 + name: value,
  780 + type:
  781 + typeValue.value == ''
  782 + ? 'org.thingsboard.rule.engine.rest.TbRestApiCallNode'
  783 + : typeValue.value,
  784 + }).then((data) => {
  785 + if (data == true) {
  786 + // createMessage.error('名称已存在');
  787 + resolve();
  788 + } else {
  789 + resolve();
  790 + }
  791 + });
  792 + } else {
  793 + resolve();
  794 + }
  795 + }
  796 + });
  797 + },
  798 + },
  799 + ];
  800 + },
  801 + },
  802 + {
  803 + field: 'restEndpointUrlPattern',
  804 + label: '端点URL模式',
  805 + colProps: { span: 12 },
  806 + required: true,
  807 + defaultValue: 'http://localhost/api',
  808 + component: 'Input',
  809 + componentProps: {
  810 + maxLength: 255,
  811 + placeholder: '请输入Endpoint URL pattern',
  812 + },
  813 + },
  814 + {
  815 + field: 'requestMethod',
  816 + component: 'Select',
  817 + label: '请求方式',
  818 + colProps: { span: 12 },
  819 + defaultValue: 'POST',
  820 + componentProps: {
  821 + placeholder: '请选择Request method',
  822 + options: [
  823 + { label: 'GET', value: 'GET' },
  824 + { label: 'POST', value: 'POST' },
  825 + { label: 'PUT', value: 'PUT' },
  826 + { label: 'DELETE', value: 'DELETE' },
  827 + ],
  828 + },
  829 + },
  830 + {
  831 + field: 'enableProxy',
  832 + label: '是否启用',
  833 + colProps: { span: 12 },
  834 + component: 'Checkbox',
  835 + renderComponentContent: '启用代理',
  836 + },
  837 +
  838 + {
  839 + field: 'proxyHost',
  840 + label: '代理主机',
  841 + colProps: { span: 12 },
  842 + required: true,
  843 + component: 'Input',
  844 + componentProps: {
  845 + maxLength: 255,
  846 + placeholder: 'http或者https开头',
  847 + },
  848 + ifShow: ({ values }) => {
  849 + return !!values.enableProxy;
  850 + },
  851 + },
  852 + {
  853 + field: 'proxyPort',
  854 + label: '代理端口',
  855 + colProps: { span: 12 },
  856 + required: true,
  857 + component: 'InputNumber',
  858 + defaultValue: 0,
  859 + componentProps: {
  860 + maxLength: 255,
  861 + placeholder: 'http或者https开头',
  862 + },
  863 + ifShow: ({ values }) => {
  864 + return !!values.enableProxy;
  865 + },
  866 + },
  867 + {
  868 + field: 'proxyUser',
  869 + label: '代理用户',
  870 + colProps: { span: 12 },
  871 + required: true,
  872 + component: 'Input',
  873 + componentProps: {
  874 + maxLength: 255,
  875 + placeholder: '请输入代理用户',
  876 + },
  877 + ifShow: ({ values }) => {
  878 + return !!values.enableProxy;
  879 + },
  880 + },
  881 + {
  882 + field: 'proxyPassword',
  883 + label: '代理密码',
  884 + colProps: { span: 12 },
  885 + required: true,
  886 + component: 'InputPassword',
  887 + componentProps: {
  888 + maxLength: 255,
  889 + placeholder: '请输入代理密码',
  890 + },
  891 + ifShow: ({ values }) => {
  892 + return !!values.enableProxy;
  893 + },
  894 + },
  895 +
  896 + {
  897 + field: 'useSystemProxyProperties',
  898 + label: '是否启用',
  899 + colProps: { span: 12 },
  900 + component: 'Checkbox',
  901 + renderComponentContent: '使用系统代理属性',
  902 + },
  903 + {
  904 + field: 'maxParallelRequestsCount',
  905 + label: '最大并行请求数',
  906 + colProps: { span: 12 },
  907 + required: true,
  908 + component: 'InputNumber',
  909 + defaultValue: 0,
  910 + componentProps: {
  911 + maxLength: 255,
  912 + },
  913 + ifShow: ({ values }) => {
  914 + return !!values.useSystemProxyProperties;
  915 + },
  916 + },
  917 + {
  918 + field: 'ignoreRequestBody',
  919 + label: '是否启用',
  920 + colProps: { span: 12 },
  921 + component: 'Checkbox',
  922 + renderComponentContent: '无请求正文',
  923 + },
  924 + {
  925 + field: 'readTimeoutMs',
  926 + label: '读取超时(毫秒)',
  927 + colProps: { span: 12 },
  928 + required: true,
  929 + component: 'InputNumber',
  930 + defaultValue: 0,
  931 + componentProps: {
  932 + maxLength: 255,
  933 + },
  934 + ifShow: ({ values }) => {
  935 + return !values.useSystemProxyProperties;
  936 + },
  937 + },
  938 + {
  939 + field: 'maxParallelRequestsCount',
  940 + label: '最大并行请求数',
  941 + colProps: { span: 12 },
  942 + required: true,
  943 + component: 'InputNumber',
  944 + defaultValue: 0,
  945 + componentProps: {
  946 + maxLength: 255,
  947 + },
  948 + ifShow: ({ values }) => {
  949 + return !values.useSystemProxyProperties;
  950 + },
  951 + },
  952 + {
  953 + field: 'headers',
  954 + label: 'Headers',
  955 + colProps: { span: 24 },
  956 + defaultValue: { 'Content-Type': 'application/json' },
  957 + component: 'JAddInput',
  958 + subLabel: '不可重复',
  959 + },
  960 +
  961 + {
  962 + field: 'useRedisQueueForMsgPersistence',
  963 + label: '是否启用',
  964 + colProps: { span: 12 },
  965 + component: 'Checkbox',
  966 + renderComponentContent: '使用redis队列进行消息持久性',
  967 + },
  968 + {
  969 + field: 'trimQueue',
  970 + label: '是否启用',
  971 + colProps: { span: 12 },
  972 + component: 'Checkbox',
  973 + renderComponentContent: '修剪redis队列',
  974 + ifShow: ({ values }) => {
  975 + return !!values.useRedisQueueForMsgPersistence;
  976 + },
  977 + },
  978 + {
  979 + field: 'maxQueueSize',
  980 + label: 'Redis队列最大数',
  981 + colProps: { span: 12 },
  982 + required: true,
  983 + component: 'InputNumber',
  984 + defaultValue: 0,
  985 + componentProps: {
  986 + maxLength: 255,
  987 + },
  988 + ifShow: ({ values }) => {
  989 + return !!values.useRedisQueueForMsgPersistence;
  990 + },
  991 + },
  992 +
  993 + {
  994 + field: 'type',
  995 + component: 'Select',
  996 + label: '凭据类型',
  997 + colProps: { span: 12 },
  998 + defaultValue: 'anonymous',
  999 + componentProps: {
  1000 + placeholder: '请选择凭据类型',
  1001 + options: [
  1002 + { label: 'Anonymous', value: 'anonymous' },
  1003 + { label: 'Basic', value: 'basic' },
  1004 + { label: 'PEM', value: 'pem' },
  1005 + ],
  1006 + },
  1007 + },
  1008 + {
  1009 + field: 'username',
  1010 + label: '用户名',
  1011 + colProps: { span: 12 },
  1012 + component: 'Input',
  1013 + required: true,
  1014 + componentProps: {
  1015 + maxLength: 255,
  1016 + placeholder: '请输入用户名',
  1017 + },
  1018 + ifShow: ({ values }) => isBasic(Reflect.get(values, 'type')),
  1019 + },
  1020 + {
  1021 + field: 'password',
  1022 + label: '密码',
  1023 + colProps: { span: 12 },
  1024 + component: 'InputPassword',
  1025 + required: true,
  1026 + componentProps: {
  1027 + maxLength: 255,
  1028 + placeholder: '请输入密码',
  1029 + },
  1030 + ifShow: ({ values }) => isBasic(Reflect.get(values, 'type')),
  1031 + },
  1032 + {
  1033 + field: '1',
  1034 + label: '',
  1035 + colProps: { span: 24 },
  1036 + component: 'Input',
  1037 + slot: 'uploadAdd1',
  1038 + ifShow: ({ values }) => isPem(Reflect.get(values, 'type')),
  1039 + },
  1040 + {
  1041 + field: '11',
  1042 + label: '',
  1043 + colProps: { span: 24 },
  1044 + component: 'Input',
  1045 + slot: 'showImg1',
  1046 + ifShow: ({ values }) => isPem(Reflect.get(values, 'type')),
  1047 + },
  1048 + {
  1049 + field: '1',
  1050 + label: '',
  1051 + colProps: { span: 24 },
  1052 + component: 'Input',
  1053 + slot: 'uploadAdd2',
  1054 + ifShow: ({ values }) => isPem(Reflect.get(values, 'type')),
  1055 + },
  1056 + {
  1057 + field: '1111',
  1058 + label: '',
  1059 + colProps: { span: 24 },
  1060 + component: 'Input',
  1061 + slot: 'showImg2',
  1062 + ifShow: ({ values }) => isPem(Reflect.get(values, 'type')),
  1063 + },
  1064 + {
  1065 + field: '1',
  1066 + label: '',
  1067 + colProps: { span: 24 },
  1068 + component: 'Input',
  1069 + slot: 'uploadAdd3',
  1070 + ifShow: ({ values }) => isPem(Reflect.get(values, 'type')),
  1071 + },
  1072 + {
  1073 + field: '111111',
  1074 + label: '',
  1075 + colProps: { span: 24 },
  1076 + component: 'Input',
  1077 + slot: 'showImg3',
  1078 + ifShow: ({ values }) => isPem(Reflect.get(values, 'type')),
  1079 + },
  1080 + {
  1081 + field: 'password',
  1082 + label: '密码',
  1083 + colProps: { span: 12 },
  1084 + component: 'InputPassword',
  1085 + componentProps: {
  1086 + maxLength: 255,
  1087 + placeholder: '请输入密码',
  1088 + },
  1089 + ifShow: ({ values }) => isPem(Reflect.get(values, 'type')),
  1090 + },
  1091 +
  1092 + {
  1093 + field: 'description',
  1094 + label: '说明',
  1095 + colProps: { span: 24 },
  1096 + component: 'InputTextArea',
  1097 + componentProps: {
  1098 + maxLength: 255,
  1099 + rows: 4,
  1100 + placeholder: '请输入说明',
  1101 + },
  1102 + },
  1103 +];