BindProtocol.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. <script setup lang="ts">
  2. import { onMounted, reactive, ref } from 'vue';
  3. import { message } from 'ant-design-vue';
  4. import { useRequest } from '@/hooks/request';
  5. import { t } from '@/i18n';
  6. import {
  7. gatewayLinkDelete,
  8. gatewayLinkGetList,
  9. gatewayLinkList,
  10. gatewayLinkProtocolReset,
  11. gatewayLinkUpdate,
  12. getDictTypeData,
  13. postProtocolCandidatesList,
  14. } from '@/api';
  15. import { DictCode } from '@/constants';
  16. import type { FormInstance } from 'ant-design-vue';
  17. import type { Rule } from 'ant-design-vue/es/form';
  18. import type { Key } from 'ant-design-vue/es/table/interface';
  19. import type {
  20. AgreementData,
  21. AgreementForm,
  22. InterfaceData,
  23. OptionItem,
  24. ProtocolItem,
  25. ProtocolsItem,
  26. RegisterGatewayForm,
  27. UseGuideStepItemProps,
  28. VerificationAgreement,
  29. } from '@/types';
  30. const { handleRequest } = useRequest();
  31. const props = defineProps<UseGuideStepItemProps<RegisterGatewayForm>>();
  32. const interfaceData = ref<InterfaceData[]>([]);
  33. const value2 = ref('2');
  34. const agreementForm = reactive<AgreementForm>({
  35. baudRate: 0,
  36. dataBit: '',
  37. parityBit: '',
  38. stopBit: '',
  39. readTimeout: 0,
  40. nextDataReadDelay: 0,
  41. nextRoundDataReadDelay: 0,
  42. });
  43. const open = ref<boolean>(false);
  44. const open1 = ref<boolean>(false);
  45. const open2 = ref<boolean>(false);
  46. const agreementData = ref<AgreementData[]>([]);
  47. const baudRatelist = ref<OptionItem<number>[]>([]);
  48. const protocolList = ref<ProtocolItem[]>([]);
  49. const formRef = ref<FormInstance>();
  50. const protocolsItem = reactive<AgreementData>({
  51. protocolType: '',
  52. station: '',
  53. name: '',
  54. protocolName: '',
  55. dev: '',
  56. dataSendInterval: 0,
  57. highFreqSendInterval: 0,
  58. protocolId: 0,
  59. isStandard: '0',
  60. });
  61. let agreementIndex: number;
  62. let protocolItem: ProtocolItem;
  63. let agreementId: number;
  64. let interfaceIndex: number;
  65. let advancedIndex: number;
  66. let verificationAgreement: VerificationAgreement[] = [];
  67. let data: ProtocolsItem[] = [];
  68. const columns = [
  69. {
  70. title: t('setupProtocol.protocolType'),
  71. dataIndex: 'protocolType',
  72. key: 'protocolType',
  73. width: 170,
  74. },
  75. {
  76. title: t('registerGateway.stationNumber'),
  77. dataIndex: 'station',
  78. key: 'station',
  79. },
  80. {
  81. title: t('registerGateway.electricMeter'),
  82. dataIndex: 'name',
  83. key: 'name',
  84. },
  85. {
  86. title: t('registerGateway.communicationProtocol'),
  87. key: 'protocolName',
  88. dataIndex: 'protocolName',
  89. width: 220,
  90. },
  91. {
  92. title: t('registerGateway.associatedEquipment'),
  93. key: 'dev',
  94. dataIndex: 'dev',
  95. },
  96. {
  97. title: t('common.operation'),
  98. key: 'action',
  99. },
  100. ];
  101. const protocolColumns = [
  102. {
  103. title: t('setupProtocol.protocolType'),
  104. dataIndex: 'protocolName',
  105. key: 'protocolName',
  106. },
  107. {
  108. title: t('registerGateway.agreementNumber'),
  109. dataIndex: 'protocolNumber',
  110. key: 'protocolNumber',
  111. },
  112. {
  113. title: t('setupProtocol.protocolName'),
  114. dataIndex: 'deviceType',
  115. key: 'deviceType',
  116. },
  117. ];
  118. const options2 = ref([
  119. {
  120. value: '1',
  121. payload: t('registerGateway.platformAgreement'),
  122. },
  123. {
  124. value: '2',
  125. payload: t('registerGateway.proprietaryProtocol'),
  126. },
  127. ]);
  128. const postGetList = () => {
  129. handleRequest(async () => {
  130. interfaceData.value = await gatewayLinkGetList(props.form.id);
  131. });
  132. };
  133. const addDelete = (value: number) => {
  134. handleRequest(async () => {
  135. await gatewayLinkDelete(value);
  136. postGetList();
  137. });
  138. };
  139. const addSlave = () => {
  140. agreementData.value.push({
  141. protocolType: '',
  142. station: '',
  143. name: '',
  144. protocolName: '',
  145. dev: '',
  146. dataSendInterval: 0,
  147. highFreqSendInterval: 0,
  148. protocolId: 0,
  149. isStandard: '0',
  150. });
  151. };
  152. const bindingAgreement = () => {
  153. data = [];
  154. formRef.value
  155. ?.validate()
  156. .then(() => {
  157. handleRequest(async () => {
  158. if (agreementData.value.length) {
  159. agreementData.value.forEach((item) => {
  160. const { protocolId, station, isStandard, dataSendInterval, highFreqSendInterval } = item;
  161. data.push({
  162. protocolId,
  163. station,
  164. isStandard,
  165. dataSendInterval,
  166. highFreqSendInterval,
  167. });
  168. });
  169. await gatewayLinkProtocolReset({
  170. linkId: agreementId,
  171. protocols: data,
  172. });
  173. const { linkName, interfaceId, gatewayId, bindState } = interfaceData.value[interfaceIndex];
  174. const { dataBit, parityBit, stopBit, baudRate, readTimeout, nextDataReadDelay, nextRoundDataReadDelay } =
  175. agreementForm;
  176. await gatewayLinkUpdate({
  177. id: agreementId,
  178. linkName,
  179. interfaceId,
  180. gatewayId,
  181. bindState,
  182. dataBit,
  183. parityBit,
  184. stopBit,
  185. baudRate,
  186. readTimeout,
  187. nextDataReadDelay,
  188. nextRoundDataReadDelay,
  189. });
  190. open.value = false;
  191. postGetList();
  192. } else {
  193. message.warning(t('registerGateway.prompt'));
  194. }
  195. });
  196. })
  197. .catch(() => {});
  198. };
  199. const deleteAgreement = (value: number) => {
  200. agreementData.value.splice(value, 1);
  201. };
  202. const advancedSettings = (value: AgreementData, index: number) => {
  203. advancedIndex = index;
  204. const { dataSendInterval, highFreqSendInterval } = value;
  205. protocolsItem.dataSendInterval = dataSendInterval;
  206. protocolsItem.highFreqSendInterval = highFreqSendInterval;
  207. open2.value = true;
  208. };
  209. const replaceAgreement = (value: number) => {
  210. agreementIndex = value;
  211. open1.value = true;
  212. handleRequest(async () => {
  213. const { records } = await postProtocolCandidatesList(
  214. {
  215. pageIndex: 1,
  216. pageSize: 10,
  217. },
  218. props.form.id,
  219. );
  220. protocolList.value = records;
  221. });
  222. };
  223. const rules1: Record<string, Rule[]> = {
  224. dataSendInterval: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
  225. highFreqSendInterval: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
  226. };
  227. const rules: Record<string, Rule[]> = {
  228. baudRate: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
  229. dataBit: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
  230. parityBit: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
  231. stopBit: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
  232. readTimeout: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
  233. nextDataReadDelay: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
  234. nextRoundDataReadDelay: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
  235. };
  236. const rowSelectedChange = (selectedRowKeys: Key[], selectedRows: ProtocolItem[]) => {
  237. protocolItem = selectedRows[0];
  238. };
  239. // 根据条件显示背景
  240. const interfaceShow = (value: string) => {
  241. return value === 'LAN' ? 'background-color: rgba(54, 207, 201, 1);' : 'background-color: rgba(89, 126, 247, 1);';
  242. };
  243. // 根据条件显示编辑/绑定文本
  244. const textShow = (value: number) => {
  245. return value === 0 ? t('common.binding') : t('common.revise');
  246. };
  247. // 显示绑定/编辑弹框
  248. const addEditor = (value: number, index: number, bindState: number) => {
  249. agreementId = value;
  250. interfaceIndex = index;
  251. open.value = true;
  252. Object.assign(agreementForm, interfaceData.value[index]);
  253. agreementData.value = [];
  254. handleRequest(async () => {
  255. const data = await getDictTypeData({
  256. dictCode: DictCode.BaudRate,
  257. });
  258. if (data.length) {
  259. baudRatelist.value = data[0].dictTypeDataList.map((item) => ({
  260. value: item.dictValueId,
  261. label: item.dictValue,
  262. }));
  263. agreementForm.baudRate = Number(data[0].dictTypeDataList[0].dictValue);
  264. }
  265. if (bindState === 1) {
  266. verificationAgreement = await gatewayLinkList(agreementId);
  267. verificationAgreement.forEach((item) => {
  268. const { protocolType, station, protocolName, dataSendInterval, highFreqSendInterval, protocolId, isStandard } =
  269. item;
  270. agreementData.value.push({
  271. name: '',
  272. dev: '',
  273. protocolType,
  274. station: String(station),
  275. protocolName,
  276. dataSendInterval,
  277. highFreqSendInterval,
  278. protocolId,
  279. isStandard: String(isStandard),
  280. });
  281. });
  282. }
  283. });
  284. };
  285. const handleOk = () => {
  286. agreementData.value[advancedIndex].dataSendInterval = protocolsItem.dataSendInterval;
  287. agreementData.value[advancedIndex].highFreqSendInterval = protocolsItem.highFreqSendInterval;
  288. open2.value = false;
  289. };
  290. const handleOk1 = () => {
  291. const { protocolName, baudRate, dataSendInterval, highFreqSendInterval, id } = protocolItem;
  292. agreementData.value[agreementIndex] = {
  293. protocolType: String(baudRate),
  294. station: '',
  295. name: '',
  296. protocolName,
  297. dev: '',
  298. dataSendInterval,
  299. highFreqSendInterval,
  300. protocolId: id,
  301. isStandard: '0',
  302. };
  303. open1.value = false;
  304. };
  305. onMounted(() => {
  306. postGetList();
  307. });
  308. </script>
  309. <template>
  310. <div>
  311. <div class="use-guide-title">{{ $t('registerGateway.bindingAgreement') }}</div>
  312. <div class="use-guide-description">文本描述!!!</div>
  313. <div class="interface-content">
  314. <ABadge v-for="(item, index) in interfaceData" :key="item.id">
  315. <template #count v-if="item.bindState === 1">
  316. <img referrerpolicy="no-referrer" src="@/assets/img/checked.png" />
  317. </template>
  318. <AFlex class="interface-div" justify="flex-start" align="center">
  319. <ARow style="width: 100%">
  320. <ACol :span="14">
  321. <AFlex justify="flex-start" align="center">
  322. <AFlex
  323. class="interface-div1"
  324. :style="interfaceShow(item.interfaceType)"
  325. justify="center"
  326. align="center"
  327. >
  328. <span class="interface-div1-span">{{ item.interfaceType }}</span>
  329. </AFlex>
  330. <div class="interface-div2">{{ item.linkName }}</div>
  331. </AFlex>
  332. </ACol>
  333. <ACol :span="10">
  334. <AFlex justify="flex-end" align="center">
  335. <AButton
  336. style="margin-top: 10px; color: #1890ff"
  337. type="link"
  338. danger
  339. @click="addEditor(item.id, index, item.bindState)"
  340. >{{ textShow(item.bindState) }}</AButton
  341. >
  342. <AButton style="margin-top: 8px; margin-right: 10px" type="link" danger @click="addDelete(item.id)">{{
  343. $t('common.delete')
  344. }}</AButton>
  345. </AFlex>
  346. </ACol>
  347. </ARow>
  348. </AFlex>
  349. </ABadge>
  350. </div>
  351. <AModal
  352. v-model:open="open"
  353. :closable="false"
  354. width="1100px"
  355. :footer="null"
  356. :mask-closable="false"
  357. :keyboard="false"
  358. >
  359. <AForm
  360. ref="formRef"
  361. class="protocol-class"
  362. :model="agreementForm"
  363. label-align="left"
  364. layout="vertical"
  365. :rules="rules"
  366. :wrapper-col="{ span: 21 }"
  367. >
  368. <ARow style="margin-top: 40px">
  369. <ACol :span="6">
  370. <AFormItem :label="$t('setupProtocol.baudRate')" name="baudRate">
  371. <ASelect
  372. v-model:value="agreementForm.baudRate"
  373. class="protocol-select"
  374. :placeholder="$t('common.plzSelect')"
  375. >
  376. <ASelectOption v-for="item in baudRatelist" :key="item.value" :value="item.label">
  377. {{ item.label }}
  378. </ASelectOption>
  379. </ASelect>
  380. </AFormItem>
  381. </ACol>
  382. <ACol :span="6">
  383. <AFormItem :label="$t('setupProtocol.dataBit')" name="dataBit">
  384. <ARadioGroup v-model:value="agreementForm.dataBit">
  385. <ARadio :value="5">5</ARadio>
  386. <ARadio :value="6">6</ARadio>
  387. <ARadio :value="7">7</ARadio>
  388. <ARadio :value="8">8</ARadio>
  389. </ARadioGroup>
  390. </AFormItem>
  391. </ACol>
  392. <ACol :span="6">
  393. <AFormItem :label="$t('setupProtocol.stopBit')" name="stopBit">
  394. <ARadioGroup v-model:value="agreementForm.stopBit">
  395. <ARadio value="1">1</ARadio>
  396. <ARadio value="1.5">1.5</ARadio>
  397. <ARadio value="2">2</ARadio>
  398. </ARadioGroup>
  399. </AFormItem>
  400. </ACol>
  401. <ACol :span="6">
  402. <AFormItem :label="$t('setupProtocol.parityBit')" name="parityBit">
  403. <ARadioGroup v-model:value="agreementForm.parityBit">
  404. <ARadio value="N">N</ARadio>
  405. <ARadio value="O">O</ARadio>
  406. <ARadio value="E">E</ARadio>
  407. </ARadioGroup>
  408. </AFormItem>
  409. </ACol>
  410. </ARow>
  411. <ARow>
  412. <ACol :span="6">
  413. <AFormItem :label="$t('setupProtocol.readTimeout')" name="readTimeout">
  414. <AInputNumber v-model:value="agreementForm.readTimeout">
  415. <template #addonAfter>ms</template>
  416. </AInputNumber>
  417. </AFormItem>
  418. </ACol>
  419. <ACol :span="6">
  420. <AFormItem :label="$t('registerGateway.nextDelayExtraction')" name="nextDataReadDelay">
  421. <AInputNumber v-model:value="agreementForm.nextDataReadDelay">
  422. <template #addonAfter>ms</template>
  423. </AInputNumber>
  424. </AFormItem>
  425. </ACol>
  426. <ACol :span="6">
  427. <AFormItem :label="$t('registerGateway.nextRoundDelayExtraction')" name="nextRoundDataReadDelay">
  428. <AInputNumber v-model:value="agreementForm.nextRoundDataReadDelay">
  429. <template #addonAfter>ms</template>
  430. </AInputNumber>
  431. </AFormItem>
  432. </ACol>
  433. </ARow>
  434. </AForm>
  435. <ATable :columns="columns" :data-source="agreementData" :pagination="false" :scroll="{ y: 350 }">
  436. <template #bodyCell="{ column, record, index }">
  437. <template v-if="column.key === 'protocolType'">
  438. <AInput disabled v-model:value="record.protocolType" />
  439. </template>
  440. <template v-else-if="column.key === 'station'">
  441. <AInput v-model:value="record.station" />
  442. </template>
  443. <template v-else-if="column.key === 'name'">
  444. <AInput disabled v-model:value="record.name" />
  445. </template>
  446. <template v-else-if="column.key === 'protocolName'">
  447. <AInputSearch disabled v-model:value="record.protocolName">
  448. <template #enterButton>
  449. <AButton style="color: #32bac0" @click="replaceAgreement(index)">{{
  450. $t('registerGateway.renegotiationAgreement')
  451. }}</AButton>
  452. </template>
  453. </AInputSearch>
  454. </template>
  455. <template v-else-if="column.key === 'action'">
  456. <AButton @click="advancedSettings(record as AgreementData, index)">{{ $t('common.settings') }}</AButton>
  457. <AButton @click="deleteAgreement(index)">{{ $t('common.delete') }}</AButton>
  458. </template>
  459. </template>
  460. </ATable>
  461. <AFlex justify="space-between">
  462. <div class="but-text" @click="addSlave">
  463. <span>{{ $t('registerGateway.addStation') }}</span>
  464. </div>
  465. <div style="margin-top: 24px">
  466. <AButton class="off-but" @click="open = false">{{ $t('common.turnOff') }}</AButton>
  467. <AButton type="primary" @click="bindingAgreement">{{ $t('common.save') }}</AButton>
  468. </div>
  469. </AFlex>
  470. </AModal>
  471. <AModal
  472. v-model:open="open1"
  473. :title="$t('registerGateway.renegotiationAgreement')"
  474. :ok-text="$t('common.confirm')"
  475. :cancel-text="$t('common.cancel')"
  476. width="1100px"
  477. @ok="handleOk1"
  478. :mask-closable="false"
  479. :keyboard="false"
  480. >
  481. <ARow style="margin-top: 40px; margin-bottom: 10px">
  482. <ACol :span="12">
  483. <ASegmented class="agreement-segmented" v-model:value="value2" :options="options2">
  484. <template #label="{ payload }">
  485. <div style="padding: 4px">
  486. <div>{{ payload }}</div>
  487. </div>
  488. </template>
  489. </ASegmented>
  490. </ACol>
  491. <ACol :span="12">
  492. <AFlex justify="flex-end">
  493. <AInput style="width: 50%" :placeholder="$t('registerGateway.pleaseProtocolName')" />
  494. </AFlex>
  495. </ACol>
  496. </ARow>
  497. <ATable
  498. :row-selection="{
  499. type: 'radio',
  500. onChange: rowSelectedChange,
  501. }"
  502. row-key="id"
  503. :columns="protocolColumns"
  504. :data-source="protocolList"
  505. />
  506. </AModal>
  507. <AModal
  508. v-model:open="open2"
  509. :title="$t('registerGateway.advancedSettingsAgreement')"
  510. :ok-text="$t('common.confirm')"
  511. :cancel-text="$t('common.cancel')"
  512. width="500px"
  513. @ok="handleOk"
  514. :mask-closable="false"
  515. :keyboard="false"
  516. >
  517. <AForm :model="protocolsItem" label-align="left" layout="vertical" :rules="rules1" :wrapper-col="{ span: 20 }">
  518. <ARow style="margin-top: 40px">
  519. <ACol :span="12">
  520. <AFormItem :label="$t('registerGateway.dataReportingFrequency')" name="dataSendInterval">
  521. <AInput v-model:value="protocolsItem.dataSendInterval" placeholder="Basic usage" /> </AFormItem
  522. ></ACol>
  523. <ACol :span="12"
  524. ><AFormItem :label="$t('registerGateway.highDataFrequencyReportingFrequency')" name="highFreqSendInterval">
  525. <AInput v-model:value="protocolsItem.highFreqSendInterval" placeholder="Basic usage" /> </AFormItem
  526. ></ACol>
  527. </ARow>
  528. </AForm>
  529. </AModal>
  530. </div>
  531. </template>
  532. <style lang="scss" scoped>
  533. .interface-content {
  534. width: 570px;
  535. margin-top: 24px;
  536. }
  537. .interface-div {
  538. width: 457px;
  539. height: 67px;
  540. margin-bottom: 14px;
  541. background-color: rgb(240 240 240 / 100%);
  542. border-radius: 8px;
  543. }
  544. .interface-div1 {
  545. width: 48px;
  546. height: 48px;
  547. margin-left: 20px;
  548. border-radius: 4px;
  549. }
  550. .interface-div1-span {
  551. font-size: 16px;
  552. font-style: normal;
  553. font-weight: 400;
  554. line-height: 18px;
  555. color: rgb(255 255 255 / 97%);
  556. }
  557. .interface-div2 {
  558. margin-left: 20px;
  559. font-size: 16px;
  560. font-style: normal;
  561. font-weight: 500;
  562. line-height: 17px;
  563. color: var(--antd-color-text-secondary);
  564. text-align: left;
  565. }
  566. .protocol-class .ant-form-item {
  567. margin-bottom: 15px !important;
  568. }
  569. .dev-list {
  570. display: flex;
  571. align-items: center; /* 垂直居中 */
  572. justify-content: center; /* 水平居中 */
  573. width: 80px;
  574. height: 32px;
  575. font-size: 14px;
  576. font-style: normal;
  577. font-weight: 400;
  578. line-height: 22px;
  579. color: #333;
  580. text-align: left;
  581. background-color: #e1f5f6;
  582. }
  583. .but-text {
  584. display: flex;
  585. align-items: center;
  586. justify-content: center;
  587. width: 108px;
  588. height: 32px;
  589. margin-top: 8px;
  590. font-size: 14px;
  591. font-style: normal;
  592. font-weight: 400;
  593. line-height: 24px;
  594. color: #32bac0;
  595. text-align: left;
  596. cursor: pointer;
  597. }
  598. .off-but {
  599. margin-right: 16px;
  600. color: #32bac0;
  601. border-color: #32bac0;
  602. }
  603. </style>