ProtocolContent.vue 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  1. <script setup lang="ts">
  2. import { computed, defineComponent, onMounted, ref, useTemplateRef } from 'vue';
  3. import { message, Modal } from 'ant-design-vue';
  4. import { debounce } from 'lodash-es';
  5. import SvgIcon from '@/components/SvgIcon.vue';
  6. import { useDictData } from '@/hooks/dict-data';
  7. import { useRequest } from '@/hooks/request';
  8. import { t } from '@/i18n';
  9. import {
  10. addProtocolParam,
  11. batchDeleleProtocolParam,
  12. getProtocolParamList,
  13. getProtocolStandardParam,
  14. updateProtocolParam,
  15. } from '@/api';
  16. import { DictCode, ProtocolRecognitionResult, ProtocolType } from '@/constants';
  17. import CustomParams from './CustomParams.vue';
  18. import GatewayParamExt from './GatewayParamExt.vue';
  19. import SelectStandardParams from './SelectStandardParams.vue';
  20. import type { FormInstance, TableProps } from 'ant-design-vue';
  21. import type { DefaultOptionType, SelectValue } from 'ant-design-vue/es/select';
  22. import type { Key, SorterResult } from 'ant-design-vue/es/table/interface';
  23. import type {
  24. CheckedType,
  25. FormRules,
  26. PageParams,
  27. PageSorts,
  28. ProtocolBaseInfo,
  29. ProtocolCandidateResult,
  30. ProtocolParamInfo,
  31. ProtocolStandardParam,
  32. } from '@/types';
  33. interface Props {
  34. info: Partial<ProtocolBaseInfo>;
  35. }
  36. const props = defineProps<Props>();
  37. const VNodes = defineComponent({
  38. props: {
  39. vnodes: {
  40. type: Object,
  41. required: true,
  42. },
  43. },
  44. render() {
  45. return this.vnodes;
  46. },
  47. });
  48. const { dictData: protocolTypes, getDictData: getProtocolTypes } = useDictData(DictCode.AllProtocolType);
  49. const { dictData: baudRateList, getDictData: getBaudRateList } = useDictData(DictCode.BaudRate);
  50. const { dictData: readContinuousAddr, getDictData: getReadContinuousAddr } = useDictData(DictCode.ReadContinuousAddr);
  51. const { dictData: byteOrderList, getDictData: getByteOrderList } = useDictData(DictCode.ByteOrder);
  52. const { dictData: addrOrderList, getDictData: getAddrOrderList } = useDictData(DictCode.AddrOrder);
  53. const isModbusRtuProtocol = computed(() => {
  54. return props.info.protocolType?.includes(ProtocolType.ModbusRTU);
  55. });
  56. const isModbusTcpProtocol = computed(() => {
  57. return props.info.protocolType?.includes(ProtocolType.ModbusTCP);
  58. });
  59. const isModbusProtocol = computed(() => {
  60. return isModbusRtuProtocol.value || isModbusTcpProtocol.value;
  61. });
  62. const isS7Protocol = computed(() => {
  63. return props.info.protocolType?.includes(ProtocolType.S7);
  64. });
  65. const formRef = ref<FormInstance>();
  66. const rules = computed<FormRules<ProtocolBaseInfo>>(() => {
  67. return {
  68. protocolName: [
  69. {
  70. required: true,
  71. message: t('common.plzEnter', { name: t('setupProtocol.protocolName') }),
  72. trigger: 'blur',
  73. },
  74. ],
  75. protocolType: [
  76. {
  77. required: true,
  78. message: t('setupProtocol.plzSelectProtocolType'),
  79. trigger: 'blur',
  80. },
  81. ],
  82. deviceType: [
  83. {
  84. required: true,
  85. message: t('common.plzSelect', { name: t('setupProtocol.deviceType') }),
  86. trigger: 'blur',
  87. },
  88. ],
  89. dataBit: [
  90. {
  91. required: true,
  92. message: t('common.plzSelect', { name: t('setupProtocol.dataBit') }),
  93. trigger: 'blur',
  94. },
  95. ],
  96. parityBit: [
  97. {
  98. required: true,
  99. message: t('common.plzSelect', { name: t('setupProtocol.parityBit') }),
  100. trigger: 'blur',
  101. },
  102. ],
  103. stopBit: [
  104. {
  105. required: true,
  106. message: t('common.plzSelect', { name: t('setupProtocol.stopBit') }),
  107. trigger: 'blur',
  108. },
  109. ],
  110. baudRate: [
  111. {
  112. required: true,
  113. message: t('common.plzSelect', { name: t('setupProtocol.baudRate') }),
  114. trigger: 'blur',
  115. },
  116. ],
  117. dataSendInterval: [
  118. {
  119. required: true,
  120. message: t('common.plzEnter', { name: t('setupProtocol.dataSendInterval') }),
  121. trigger: 'blur',
  122. },
  123. ],
  124. highFreqSendInterval: [
  125. {
  126. required: true,
  127. message: t('common.plzEnter', { name: t('setupProtocol.highFreqSendInterval') }),
  128. trigger: 'blur',
  129. },
  130. ],
  131. readTimeout: [
  132. {
  133. required: true,
  134. message: t('common.plzEnter', { name: t('setupProtocol.readTimeout') }),
  135. trigger: 'blur',
  136. },
  137. ],
  138. nextDataReadDelay: [
  139. {
  140. required: true,
  141. message: t('common.plzEnter', { name: t('setupProtocol.nextDataReadDelay') }),
  142. trigger: 'blur',
  143. },
  144. ],
  145. nextRoundDataReadDelay: [
  146. {
  147. required: true,
  148. message: t('common.plzEnter', { name: t('setupProtocol.nextRoundDataReadDelay') }),
  149. trigger: 'blur',
  150. },
  151. ],
  152. byteOrder: [
  153. {
  154. required: true,
  155. message: t('common.plzSelect', { name: t('setupProtocol.byteOrder') }),
  156. trigger: 'blur',
  157. },
  158. ],
  159. addrOrder: [
  160. {
  161. required: true,
  162. message: t('common.plzSelect', { name: t('setupProtocol.addrOrder') }),
  163. trigger: 'blur',
  164. },
  165. ],
  166. };
  167. });
  168. const validateProtocolInfo = async () => {
  169. await formRef.value?.validate();
  170. };
  171. onMounted(() => {
  172. getProtocolTypes();
  173. getBaudRateList();
  174. getReadContinuousAddr();
  175. getByteOrderList();
  176. getAddrOrderList();
  177. getCurrentProtocolParams();
  178. });
  179. const handleReadContinuousAddrChange = (checked: CheckedType) => {
  180. const [first, second] = readContinuousAddr.value;
  181. if (checked === first?.dictValue) {
  182. props.info.readContinuousAddrCode = Number(first?.dictEngValue);
  183. } else {
  184. props.info.readContinuousAddrCode = Number(second?.dictEngValue);
  185. }
  186. };
  187. const handleByteOrderChange = (_value: SelectValue, option: DefaultOptionType) => {
  188. props.info.byteOrderCode = option.key;
  189. };
  190. const handleAddrOrderChange = (_value: SelectValue, option: DefaultOptionType) => {
  191. props.info.addrOrderCode = option.key;
  192. };
  193. const paramList = ref<ProtocolParamInfo[]>([]);
  194. const paramTotal = ref(0);
  195. const paramNameOrCode = ref('');
  196. const pageParams = ref<PageParams>({
  197. pageIndex: 1,
  198. pageSize: 10,
  199. pageSorts: [
  200. {
  201. column: 'id',
  202. asc: true,
  203. },
  204. ],
  205. });
  206. const { isLoading, handleRequest } = useRequest();
  207. let recognizeResult: string[] = [];
  208. const getCurrentProtocolParams = () => {
  209. handleRequest(async () => {
  210. const { id } = props.info;
  211. if (!id) {
  212. return;
  213. }
  214. const { records, total } = await getProtocolParamList({
  215. ...pageParams.value,
  216. baseInfoId: id,
  217. paramName: paramNameOrCode.value,
  218. paramCode: paramNameOrCode.value,
  219. recognizeResult,
  220. });
  221. paramList.value = records;
  222. paramTotal.value = total;
  223. });
  224. };
  225. const handleParamNameOrCodeChange = debounce(() => {
  226. pageParams.value.pageIndex = 1;
  227. selectedParamIds.value = [];
  228. getCurrentProtocolParams();
  229. }, 300);
  230. const handleParamTableChange: TableProps['onChange'] = (pagination, filters, sorter) => {
  231. recognizeResult = (filters.recognizeResult as string[] | null) || [];
  232. pageParams.value.pageIndex = pagination.current || 1;
  233. pageParams.value.pageSize = pagination.pageSize || 10;
  234. let sorts: SorterResult[];
  235. if (Array.isArray(sorter)) {
  236. sorts = sorter;
  237. } else {
  238. sorts = [sorter];
  239. }
  240. sorts = sorts.filter((item) => item.field !== undefined && item.order !== undefined);
  241. if (sorts.length) {
  242. pageParams.value.pageSorts = sorts.map<PageSorts>((item) => ({
  243. column: item.field as string,
  244. asc: item.order === 'ascend',
  245. }));
  246. } else {
  247. pageParams.value.pageSorts = [
  248. {
  249. column: 'id',
  250. asc: true,
  251. },
  252. ];
  253. }
  254. selectedParamIds.value = [];
  255. getCurrentProtocolParams();
  256. };
  257. const selectedParamIds = ref<number[]>([]);
  258. const handleParamSelectChange = (selectedRowKeys: Key[]) => {
  259. selectedParamIds.value = selectedRowKeys as number[];
  260. };
  261. const deleteSelectedParams = () => {
  262. Modal.confirm({
  263. title: t('setupProtocol.confirmDeleteParams'),
  264. async onOk() {
  265. try {
  266. if (props.info.id) {
  267. await batchDeleleProtocolParam(selectedParamIds.value);
  268. } else {
  269. deleteLocalParams();
  270. }
  271. selectedParamIds.value = [];
  272. message.success(t('setupProtocol.deleteParamsSuccessful'));
  273. pageParams.value.pageIndex = 1;
  274. getCurrentProtocolParams();
  275. } catch (err) {
  276. message.error((err as Error).message);
  277. console.error(err);
  278. }
  279. },
  280. });
  281. };
  282. const filterProtocolCandidateResult = (input: string, option: ProtocolCandidateResult) => {
  283. return option.platformProtocolGatewayParamName.toLowerCase().indexOf(input.toLowerCase()) >= 0;
  284. };
  285. const handleProtocolCandidateChange = (value: SelectValue, params: ProtocolParamInfo) => {
  286. handleRequest(async () => {
  287. const paramInfo = await getProtocolStandardParam(value as number);
  288. await updateProtocolParam({
  289. ...paramInfo,
  290. id: params.id,
  291. });
  292. getCurrentProtocolParams();
  293. });
  294. };
  295. const customParamsRef = useTemplateRef('customParams');
  296. const selectStandardParamsRef = useTemplateRef('selectStandardParams');
  297. const addCustomParams = () => {
  298. customParamsRef.value?.setIsAddParams(true);
  299. customParamsRef.value?.showView();
  300. };
  301. const addStandardParams = () => {
  302. selectStandardParamsRef.value?.setIsAddParams(true);
  303. selectStandardParamsRef.value?.showView();
  304. };
  305. const selectedParamId = ref<number>();
  306. const changeToCustomParams = (params: ProtocolParamInfo) => {
  307. selectedParamId.value = params.id;
  308. customParamsRef.value?.setIsAddParams(false);
  309. customParamsRef.value?.showView();
  310. // 延时执行自定义参数表单的初始化,避免表单重置时会保留首次初始化的值,而不是默认的空值
  311. setTimeout(() => {
  312. customParamsRef.value?.setParamsForm(params);
  313. }, 0);
  314. };
  315. const changeToStandardParams = (params: ProtocolParamInfo) => {
  316. selectedParamId.value = params.id;
  317. selectStandardParamsRef.value?.setIsAddParams(false);
  318. selectStandardParamsRef.value?.showView();
  319. };
  320. const addLocalStandardParams = (params: ProtocolStandardParam[]) => {
  321. const allParamIds = paramList.value.map((item) => item.id);
  322. const nonRepeatingParams = params.filter((item) => !allParamIds.includes(item.id));
  323. paramList.value.push(...(nonRepeatingParams as unknown as ProtocolParamInfo[]));
  324. if (nonRepeatingParams.length < params.length) {
  325. message.info(t('setupProtocol.paramsHaveFilteredOut'));
  326. }
  327. };
  328. const editLocalStandardParam = (param: ProtocolStandardParam) => {
  329. const allParamIds = paramList.value.map((item) => item.id);
  330. if (allParamIds.includes(param.id)) {
  331. message.error(t('setupProtocol.paramAlreadyExists'));
  332. return;
  333. }
  334. const selectedParamIndex = paramList.value.findIndex((item) => item.id === selectedParamId.value);
  335. paramList.value.splice(selectedParamIndex, 1, param as unknown as ProtocolParamInfo);
  336. };
  337. const addLocalCustomParam = (param: Partial<ProtocolParamInfo>) => {
  338. paramList.value.push({
  339. ...param,
  340. id: Date.now(),
  341. } as ProtocolParamInfo);
  342. };
  343. const deleteLocalParams = () => {
  344. paramList.value = paramList.value.filter((item) => !selectedParamIds.value.includes(item.id));
  345. };
  346. const submitLocalParams = async () => {
  347. if (props.info.id) {
  348. for (const item of paramList.value) {
  349. await addProtocolParam({
  350. ...item,
  351. baseInfoId: props.info.id,
  352. });
  353. }
  354. }
  355. };
  356. /**
  357. * 协议必须至少配置一条参数,此函数用于提供给外部进行判断是否可以提交协议的创建、修改等操作
  358. */
  359. const isAtLeastOneParam = () => {
  360. let isAtLeastOne: boolean;
  361. if (props.info.id) {
  362. isAtLeastOne = paramTotal.value > 0;
  363. } else {
  364. isAtLeastOne = paramList.value.length > 0;
  365. }
  366. if (!isAtLeastOne) {
  367. throw new Error(t('setupProtocol.protocolAtLeastOneParam'));
  368. }
  369. };
  370. defineExpose({
  371. validateProtocolInfo,
  372. isAtLeastOneParam,
  373. submitLocalParams,
  374. });
  375. </script>
  376. <template>
  377. <div>
  378. <AForm ref="formRef" :model="info" :rules="rules" layout="vertical">
  379. <div class="protocol-label">{{ $t('common.basicInfo') }}</div>
  380. <ARow :gutter="33">
  381. <ACol flex="20%">
  382. <AFormItem :label="$t('setupProtocol.protocolName')" name="protocolName">
  383. <AInput v-model:value="info.protocolName" :placeholder="$t('common.plzEnter')" />
  384. </AFormItem>
  385. </ACol>
  386. <ACol flex="20%">
  387. <AFormItem :label="$t('setupProtocol.protocolType')" name="protocolType">
  388. <ASelect v-model:value="info.protocolType" :placeholder="$t('common.plzSelect')" disabled>
  389. <ASelectOption v-for="item in protocolTypes" :key="item.dictValueId" :value="item.dictValue">
  390. {{ item.dictValue }}
  391. </ASelectOption>
  392. </ASelect>
  393. </AFormItem>
  394. </ACol>
  395. <ACol flex="20%">
  396. <AFormItem :label="$t('setupProtocol.deviceType')" name="deviceType">
  397. <ASelect v-model:value="info.deviceType" :placeholder="$t('common.plzSelect')" disabled>
  398. <!-- <ASelectOption :value=""></ASelectOption> -->
  399. </ASelect>
  400. </AFormItem>
  401. </ACol>
  402. <ACol v-if="isModbusRtuProtocol" flex="20%">
  403. <AFormItem :label="$t('setupProtocol.dataBit')" name="dataBit">
  404. <ARadioGroup v-model:value="info.dataBit">
  405. <ARadio :value="5">5</ARadio>
  406. <ARadio :value="6">6</ARadio>
  407. <ARadio :value="7">7</ARadio>
  408. <ARadio :value="8">8</ARadio>
  409. </ARadioGroup>
  410. </AFormItem>
  411. </ACol>
  412. <ACol v-if="isModbusRtuProtocol" flex="20%">
  413. <AFormItem :label="$t('setupProtocol.parityBit')" name="parityBit">
  414. <ARadioGroup v-model:value="info.parityBit">
  415. <ARadio value="N">N</ARadio>
  416. <ARadio value="O">O</ARadio>
  417. <ARadio value="E">E</ARadio>
  418. </ARadioGroup>
  419. </AFormItem>
  420. </ACol>
  421. <ACol v-if="isModbusRtuProtocol" flex="20%">
  422. <AFormItem :label="$t('setupProtocol.stopBit')" name="stopBit">
  423. <ARadioGroup v-model:value="info.stopBit">
  424. <ARadio value="1">1</ARadio>
  425. <ARadio value="1.5">1.5</ARadio>
  426. <ARadio value="2">2</ARadio>
  427. </ARadioGroup>
  428. </AFormItem>
  429. </ACol>
  430. <ACol v-if="isModbusRtuProtocol" flex="20%">
  431. <AFormItem :label="$t('setupProtocol.baudRate')" name="baudRate">
  432. <ASelect v-model:value="info.baudRate" :placeholder="$t('common.plzSelect')">
  433. <ASelectOption v-for="item in baudRateList" :key="item.dictValueId" :value="Number(item.dictValue)">
  434. {{ item.dictValue }}
  435. </ASelectOption>
  436. </ASelect>
  437. </AFormItem>
  438. </ACol>
  439. <ACol flex="20%">
  440. <AFormItem :label="$t('setupProtocol.dataSendInterval')" name="dataSendInterval">
  441. <AInputNumber
  442. v-model:value="info.dataSendInterval"
  443. :placeholder="$t('common.plzEnter')"
  444. addon-after="s"
  445. :min="0"
  446. :precision="0"
  447. />
  448. </AFormItem>
  449. </ACol>
  450. <ACol flex="20%">
  451. <AFormItem :label="$t('setupProtocol.highFreqSendInterval')" name="highFreqSendInterval">
  452. <AInputNumber
  453. v-model:value="info.highFreqSendInterval"
  454. :placeholder="$t('common.plzEnter')"
  455. addon-after="s"
  456. :min="0"
  457. :precision="0"
  458. />
  459. </AFormItem>
  460. </ACol>
  461. <ACol flex="20%">
  462. <AFormItem :label="$t('setupProtocol.readTimeout')" name="readTimeout">
  463. <AInputNumber
  464. v-model:value="info.readTimeout"
  465. :placeholder="$t('common.plzEnter')"
  466. addon-after="ms"
  467. :min="0"
  468. :precision="0"
  469. />
  470. </AFormItem>
  471. </ACol>
  472. <ACol flex="20%">
  473. <AFormItem :label="$t('setupProtocol.nextDataReadDelay')" name="nextDataReadDelay">
  474. <AInputNumber
  475. v-model:value="info.nextDataReadDelay"
  476. :placeholder="$t('common.plzEnter')"
  477. addon-after="ms"
  478. :min="0"
  479. :precision="0"
  480. />
  481. </AFormItem>
  482. </ACol>
  483. <ACol flex="20%">
  484. <AFormItem :label="$t('setupProtocol.nextRoundDataReadDelay')" name="nextRoundDataReadDelay">
  485. <AInputNumber
  486. v-model:value="info.nextRoundDataReadDelay"
  487. :placeholder="$t('common.plzEnter')"
  488. addon-after="ms"
  489. :min="0"
  490. :precision="0"
  491. />
  492. </AFormItem>
  493. </ACol>
  494. <ACol v-if="!isS7Protocol" flex="20%">
  495. <AFormItem :label="$t('setupProtocol.readContinuousAddr')" name="readContinuousAddr">
  496. <ASwitch
  497. v-model:checked="info.readContinuousAddr"
  498. :checked-value="readContinuousAddr[1]?.dictValue"
  499. :un-checked-value="readContinuousAddr[0]?.dictValue"
  500. @change="handleReadContinuousAddrChange"
  501. />
  502. </AFormItem>
  503. </ACol>
  504. <ACol v-if="!isS7Protocol" flex="20%">
  505. <AFormItem :label="$t('setupProtocol.readContinuousAddrLength')" name="readContinuousAddrLength">
  506. <AInputNumber
  507. v-model:value="info.readContinuousAddrLength"
  508. :placeholder="$t('common.plzEnter')"
  509. :min="0"
  510. :precision="0"
  511. />
  512. </AFormItem>
  513. </ACol>
  514. </ARow>
  515. <div v-if="!isS7Protocol" class="protocol-label advanced-label">{{ $t('common.advancedSettings') }}</div>
  516. <ARow v-if="!isS7Protocol" :gutter="33">
  517. <ACol flex="20%">
  518. <AFormItem :label="$t('setupProtocol.byteOrder')" name="byteOrder">
  519. <ASelect
  520. v-model:value="info.byteOrder"
  521. :placeholder="$t('common.plzSelect')"
  522. @change="handleByteOrderChange"
  523. >
  524. <ASelectOption v-for="item in byteOrderList" :key="item.dictEngValue" :value="item.dictValue">
  525. {{ item.dictValue }}
  526. </ASelectOption>
  527. </ASelect>
  528. </AFormItem>
  529. </ACol>
  530. <ACol flex="20%">
  531. <AFormItem :label="$t('setupProtocol.addrOrder')" name="addrOrder">
  532. <ASelect
  533. v-model:value="info.addrOrder"
  534. :placeholder="$t('common.plzSelect')"
  535. @change="handleAddrOrderChange"
  536. >
  537. <ASelectOption v-for="item in addrOrderList" :key="item.dictEngValue" :value="item.dictValue">
  538. {{ item.dictValue }}
  539. </ASelectOption>
  540. </ASelect>
  541. </AFormItem>
  542. </ACol>
  543. </ARow>
  544. </AForm>
  545. <div class="protocol-label params-label">
  546. {{ $t('setupProtocol.protocolParams') }}
  547. <span class="protocol-params-tip">
  548. <SvgIcon name="info-cirlce-o" />
  549. {{ $t('setupProtocol.protocolTypeTipForPlc') }}
  550. </span>
  551. </div>
  552. <div class="protocol-params-query">
  553. <AInput
  554. v-model:value="paramNameOrCode"
  555. class="protocol-params-input"
  556. :placeholder="$t('setupProtocol.plzEnterParamOrCode')"
  557. allow-clear
  558. @change="handleParamNameOrCodeChange"
  559. >
  560. <template #prefix>
  561. <SvgIcon name="search-o" :size="12" color="#B2B2B2" />
  562. </template>
  563. </AInput>
  564. <div>
  565. <AButton danger :disabled="selectedParamIds.length === 0" @click="deleteSelectedParams">
  566. {{ $t('setupProtocol.deleteSelected') }}
  567. </AButton>
  568. <AButton @click="addCustomParams">{{ $t('setupProtocol.addCustomParams') }}</AButton>
  569. <AButton @click="addStandardParams" type="primary">
  570. {{ $t('setupProtocol.addStandardParams') }}
  571. </AButton>
  572. </div>
  573. </div>
  574. <ATable
  575. :data-source="paramList"
  576. row-key="id"
  577. :row-selection="{ selectedRowKeys: selectedParamIds, onChange: handleParamSelectChange }"
  578. :scroll="{ x: 2800 }"
  579. :loading="isLoading"
  580. :pagination="{
  581. current: pageParams.pageIndex,
  582. pageSize: pageParams.pageSize,
  583. total: paramTotal,
  584. showSizeChanger: true,
  585. hideOnSinglePage: false,
  586. showTotal: (total) => $t('common.pageTotal', { total }),
  587. }"
  588. @change="handleParamTableChange"
  589. >
  590. <ATableColumn
  591. :title="$t('setupProtocol.protocolParamFields.customParamName')"
  592. data-index="paramName"
  593. :width="180"
  594. fixed="left"
  595. />
  596. <ATableColumn
  597. :title="$t('setupProtocol.protocolParamFields.result')"
  598. data-index="recognizeResult"
  599. :width="110"
  600. :sorter="true"
  601. :filters="[
  602. { text: $t('setupProtocol.recognizeSuccess'), value: ProtocolRecognitionResult.Success },
  603. { text: $t('setupProtocol.recognizeMultipleResults'), value: ProtocolRecognitionResult.MultipleResults },
  604. { text: $t('setupProtocol.recognizeFailed'), value: ProtocolRecognitionResult.Unrecognized },
  605. { text: $t('setupProtocol.recognizeNull'), value: '-' },
  606. ]"
  607. >
  608. <template #default="{ record }">
  609. <div
  610. v-if="record.recognizeResult"
  611. :class="[
  612. 'protocol-recognize-result',
  613. {
  614. 'protocol-recognize-success': record.recognizeResult === ProtocolRecognitionResult.Success,
  615. 'protocol-recognize-unrecognized': record.recognizeResult === ProtocolRecognitionResult.Unrecognized,
  616. 'protocol-recognize-multiple-results':
  617. record.recognizeResult === ProtocolRecognitionResult.MultipleResults,
  618. },
  619. ]"
  620. >
  621. {{ record.recognizeResult }}
  622. </div>
  623. </template>
  624. </ATableColumn>
  625. <ATableColumn
  626. class="table-column-item-required"
  627. :title="$t('setupProtocol.protocolParamFields.platformParamName')"
  628. data-index="platformParamName"
  629. :width="188"
  630. >
  631. <template #default="{ record }">
  632. <a v-if="record.platformParamName && !record.candidateResults" @click="changeToStandardParams(record)">
  633. {{ record.platformParamName }}
  634. </a>
  635. <ADropdown v-else-if="record.recognizeResult === ProtocolRecognitionResult.Unrecognized">
  636. <template #overlay>
  637. <AMenu>
  638. <AMenuItem key="select" @click="changeToStandardParams(record)">
  639. {{ $t('setupProtocol.selectFromProtocolLibrary') }}
  640. </AMenuItem>
  641. <AMenuItem key="custom" @click="changeToCustomParams(record)">
  642. {{ $t('common.custom') }}
  643. </AMenuItem>
  644. </AMenu>
  645. </template>
  646. <AButton class="manually-match-button">
  647. {{ $t('setupProtocol.manuallyMatch') }}
  648. <SvgIcon name="down" />
  649. </AButton>
  650. </ADropdown>
  651. <ASelect
  652. v-else-if="record.recognizeResult === ProtocolRecognitionResult.MultipleResults"
  653. class="multiple-results-select"
  654. :value="record.platformParamName || undefined"
  655. :options="JSON.parse(record.candidateResults || '[]')"
  656. :field-names="{ label: 'platformProtocolGatewayParamName', value: 'platformProtocolId' }"
  657. :dropdown-match-select-width="176"
  658. :placeholder="$t('common.plzSelect')"
  659. show-search
  660. :filter-option="filterProtocolCandidateResult"
  661. @change="handleProtocolCandidateChange($event, record)"
  662. >
  663. <template #dropdownRender="{ menuNode: menu }">
  664. <div class="multiple-results-select-header">
  665. <div class="multiple-results-select-button" @click="changeToStandardParams(record)">
  666. {{ $t('setupProtocol.selectFromProtocolLibrary') }}
  667. </div>
  668. <div class="multiple-results-select-button" @click="changeToCustomParams(record)">
  669. {{ $t('common.custom') }}
  670. </div>
  671. </div>
  672. <VNodes :vnodes="menu" />
  673. </template>
  674. </ASelect>
  675. </template>
  676. </ATableColumn>
  677. <ATableColumn
  678. class="table-column-item-required"
  679. :title="$t('setupProtocol.protocolParamFields.platformParamCode')"
  680. data-index="platformParamCode"
  681. :width="280"
  682. />
  683. <ATableColumn
  684. class="table-column-item-required"
  685. :title="$t('setupProtocol.protocolParamFields.gatewayParamName')"
  686. data-index="gatewayParamName"
  687. :width="200"
  688. >
  689. <template #default="{ record }">
  690. <GatewayParamExt v-if="record.gatewayParamExt" :ext-info="JSON.parse(record.gatewayParamExt || '{}')" />
  691. <div v-else>{{ record.gatewayParamName }}</div>
  692. </template>
  693. </ATableColumn>
  694. <ATableColumn
  695. class="table-column-item-required"
  696. :title="$t('setupProtocol.protocolParamFields.gatewayParamCode')"
  697. data-index="gatewayParamCode"
  698. :width="200"
  699. />
  700. <ATableColumn :title="$t('setupProtocol.protocolParamFields.unit')" data-index="unit" />
  701. <ATableColumn :title="$t('setupProtocol.protocolParamFields.module')" data-index="module" />
  702. <ATableColumn
  703. class="table-column-item-required"
  704. :title="$t('setupProtocol.protocolParamFields.readWriteType')"
  705. data-index="readWriteType"
  706. />
  707. <ATableColumn
  708. v-if="isS7Protocol"
  709. class="table-column-item-required"
  710. :title="$t('setupProtocol.protocolParamFields.registerType')"
  711. data-index="registerType"
  712. />
  713. <ATableColumn
  714. v-if="isS7Protocol"
  715. class="table-column-item-required"
  716. :title="$t('setupProtocol.protocolParamFields.addrNumber')"
  717. data-index="addrNumber"
  718. />
  719. <ATableColumn
  720. v-if="isModbusProtocol"
  721. :title="$t('setupProtocol.protocolParamFields.readFunctionCode')"
  722. data-index="readFuncCode"
  723. />
  724. <ATableColumn
  725. v-if="isModbusProtocol"
  726. :title="$t('setupProtocol.protocolParamFields.writeFunctionCode')"
  727. data-index="writeFuncCode"
  728. />
  729. <ATableColumn
  730. class="table-column-item-required"
  731. :title="$t('setupProtocol.protocolParamFields.registerAddress')"
  732. data-index="registerAddr"
  733. />
  734. <ATableColumn
  735. class="table-column-item-required"
  736. :title="$t('setupProtocol.protocolParamFields.parsingType')"
  737. data-index="parsingType"
  738. :width="150"
  739. />
  740. <ATableColumn
  741. v-if="isS7Protocol"
  742. class="table-column-item-required"
  743. :title="$t('setupProtocol.protocolParamFields.wordLength')"
  744. data-index="wordLength"
  745. />
  746. <ATableColumn
  747. v-if="isS7Protocol"
  748. :title="$t('setupProtocol.protocolParamFields.quantity')"
  749. data-index="quantity"
  750. />
  751. <ATableColumn
  752. v-if="isModbusProtocol"
  753. class="table-column-item-required"
  754. :title="$t('setupProtocol.protocolParamFields.addressLength')"
  755. data-index="addrLength"
  756. />
  757. <ATableColumn
  758. v-if="isModbusProtocol"
  759. :title="$t('setupProtocol.protocolParamFields.dataType')"
  760. data-index="dataType"
  761. />
  762. <ATableColumn :title="$t('setupProtocol.protocolParamFields.coefficient')" data-index="coefficient" />
  763. <ATableColumn
  764. :title="$t('setupProtocol.protocolParamFields.isHighFrequencyParameter')"
  765. data-index="isHighFreqParam"
  766. :width="150"
  767. />
  768. <ATableColumn
  769. :title="$t('setupProtocol.protocolParamFields.writeCalculationFormula')"
  770. data-index="writeCalcFormula"
  771. />
  772. <ATableColumn
  773. :title="$t('setupProtocol.protocolParamFields.readCalculationFormula')"
  774. data-index="readCalcFormula"
  775. />
  776. <ATableColumn :title="$t('setupProtocol.protocolParamFields.decimalPlaces')" data-index="decimalPlace" />
  777. </ATable>
  778. <CustomParams
  779. ref="customParams"
  780. :protocol-id="info.id"
  781. :param-id="selectedParamId"
  782. :is-modbus-rtu-protocol="isModbusRtuProtocol"
  783. :is-modbus-tcp-protocol="isModbusTcpProtocol"
  784. :is-s7-protocol="isS7Protocol"
  785. @add-local-param="addLocalCustomParam"
  786. @refresh-list="getCurrentProtocolParams"
  787. />
  788. <SelectStandardParams
  789. ref="selectStandardParams"
  790. :protocol-id="info.id"
  791. :param-id="selectedParamId"
  792. @add-local-params="addLocalStandardParams"
  793. @edit-local-param="editLocalStandardParam"
  794. @refresh-list="getCurrentProtocolParams"
  795. />
  796. </div>
  797. </template>
  798. <style lang="scss" scoped>
  799. :deep(.ant-form) {
  800. .ant-form-item {
  801. width: 192px;
  802. }
  803. .ant-input-number-group .ant-input-number-group-addon {
  804. padding-inline: 14px;
  805. color: #333;
  806. background-color: initial;
  807. }
  808. .ant-radio-wrapper span.ant-radio + * {
  809. padding-inline: 6px;
  810. }
  811. .ant-input-number {
  812. width: 100%;
  813. }
  814. .ant-switch {
  815. height: 24px;
  816. }
  817. .ant-switch-handle {
  818. width: 20px;
  819. height: 20px;
  820. }
  821. .ant-switch.ant-switch-checked .ant-switch-handle {
  822. inset-inline-start: calc(100% - 22px);
  823. }
  824. }
  825. .protocol-label {
  826. padding-left: 8px;
  827. margin-bottom: 24px;
  828. font-size: 16px;
  829. font-weight: 500;
  830. line-height: 24px;
  831. color: #000;
  832. border-left: 2px solid var(--antd-color-primary);
  833. &.advanced-label,
  834. &.params-label {
  835. margin-top: 16px;
  836. }
  837. &.params-label {
  838. display: flex;
  839. align-items: center;
  840. }
  841. }
  842. .protocol-params-input {
  843. width: 256px;
  844. }
  845. .protocol-params-tip {
  846. display: inline-flex;
  847. align-items: center;
  848. height: 22px;
  849. font-size: 12px;
  850. font-weight: 500;
  851. line-height: 22px;
  852. color: #b2b2b2;
  853. i {
  854. margin-right: 5px;
  855. margin-left: 16px;
  856. }
  857. }
  858. .protocol-params-query {
  859. display: flex;
  860. justify-content: space-between;
  861. margin-bottom: 16px;
  862. button + button {
  863. margin-left: 16px;
  864. }
  865. }
  866. :deep(.ant-table-wrapper) .ant-table-container {
  867. .ant-table-thead > tr > th {
  868. color: #333;
  869. background: #f5f6f7;
  870. border-bottom: none;
  871. }
  872. .ant-table-thead
  873. > tr
  874. > th.ant-table-cell:not(:last-child, .ant-table-selection-column, .ant-table-row-expand-icon-cell, [colspan]) {
  875. &::before {
  876. background-color: transparent;
  877. }
  878. &.table-column-item-required::before {
  879. position: initial;
  880. width: auto;
  881. font-size: 14px;
  882. line-height: 24px;
  883. color: #f56c6c;
  884. content: '*';
  885. transform: initial;
  886. }
  887. }
  888. }
  889. .protocol-recognize-result {
  890. height: 24px;
  891. padding: 2px 8px;
  892. font-size: 12px;
  893. line-height: 20px;
  894. text-align: center;
  895. border: 1px solid var(--antd-color-border);
  896. border-radius: 4px;
  897. &.protocol-recognize-success {
  898. color: #52c41a;
  899. background: #f6ffed;
  900. border-color: #b7eb8f;
  901. }
  902. &.protocol-recognize-unrecognized {
  903. color: #f56c6c;
  904. background: #fff1f0;
  905. border-color: #ffa39e;
  906. }
  907. &.protocol-recognize-multiple-results {
  908. color: var(--antd-color-primary);
  909. background: var(--antd-color-primary-opacity-10);
  910. border-color: var(--antd-color-primary-opacity-60);
  911. }
  912. }
  913. .manually-match-button {
  914. display: flex;
  915. align-items: center;
  916. justify-content: space-between;
  917. width: 100%;
  918. color: #333;
  919. i {
  920. color: #8c94a8;
  921. }
  922. &:hover {
  923. color: #333;
  924. }
  925. }
  926. .multiple-results-select {
  927. width: 100%;
  928. }
  929. .multiple-results-select-header {
  930. display: flex;
  931. align-items: center;
  932. justify-content: space-between;
  933. padding: 12px;
  934. padding-top: 8px;
  935. margin-bottom: 8px;
  936. border-bottom: 1px solid #e4e7ed;
  937. .multiple-results-select-button {
  938. height: 24px;
  939. padding: 1px 8px;
  940. font-size: 14px;
  941. line-height: 22px;
  942. text-shadow: 0 -2px 12px rgb(0 0 0 / 10%);
  943. cursor: pointer;
  944. border-radius: 4px;
  945. box-shadow: 0 -2px 12px 0 rgb(0 0 0 / 10%);
  946. &:first-child {
  947. color: #fff;
  948. background: var(--antd-color-primary);
  949. }
  950. &:last-child {
  951. color: var(--antd-color-primary);
  952. background: #fff;
  953. border: 1px solid var(--antd-color-primary);
  954. }
  955. }
  956. }
  957. </style>