index.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767
  1. <template>
  2. <div class="ele-body">
  3. <el-card shadow="never" v-loading="loading">
  4. <ele-split-layout
  5. width="230px"
  6. allow-collapse
  7. :right-style="{ overflow: 'hidden' }"
  8. >
  9. <div>
  10. <div class="ele-border-lighter sys-organization-list">
  11. <div
  12. class="add"
  13. style="
  14. display: flex;
  15. justify-content: flex-end;
  16. margin-bottom: 10px;
  17. "
  18. >
  19. <el-button type="primary" @click="openEditType('add')"
  20. >添 加</el-button
  21. >
  22. </div>
  23. <el-tree
  24. :data="typeTree"
  25. default-expand-all
  26. @node-click="handleNodeClick"
  27. highlight-current
  28. ref="treeRef"
  29. node-key="id"
  30. :expand-on-click-node="false"
  31. >
  32. <span class="custom-tree-node" slot-scope="{ node, data }">
  33. <div>
  34. <span>{{ data.type == 'templateStyle' ? '模板-' : '' }}</span>
  35. {{ node.label }}</div
  36. >
  37. <span>
  38. <div class="edit-box">
  39. <!-- <i
  40. v-if="node.type == 'templateStyle'"
  41. @click.stop="openEditType('edit', node)"
  42. class="el-icon-edit"
  43. ></i> -->
  44. <el-popconfirm
  45. v-if="node.isLeaf"
  46. title="确定删除吗?"
  47. @confirm="deleteTypeNode(node)"
  48. >
  49. <i
  50. slot="reference"
  51. @click.stop
  52. class="el-icon-delete"
  53. style="color: #f56c6c"
  54. ></i>
  55. </el-popconfirm>
  56. </div>
  57. </span>
  58. </span>
  59. </el-tree>
  60. </div>
  61. </div>
  62. <template v-slot:content>
  63. <search @search="reload" />
  64. <ele-pro-table
  65. ref="table"
  66. :columns="columns"
  67. :datasource="datasource"
  68. :cache-key="cacheKeyUrl"
  69. row-key="id"
  70. :initLoad="false"
  71. >
  72. <template v-slot:toolbar>
  73. <el-button
  74. size="small"
  75. type="primary"
  76. icon="el-icon-plus"
  77. class="ele-btn-icon"
  78. @click="addPermit(null, 'add', '新增记录规则')"
  79. >
  80. 新建
  81. </el-button>
  82. </template>
  83. <!-- <template v-slot:status="{ row }"> </template> -->
  84. <template v-slot:action="{ row }">
  85. <el-link
  86. type="primary"
  87. :underline="false"
  88. icon="el-icon-document-copy"
  89. @click="addPermit(row, 'clone', '克隆记录规则')"
  90. >
  91. 克隆
  92. </el-link>
  93. <el-link
  94. v-if="row.publishStatus === 1 && !row.isUpdatedVersion"
  95. type="success"
  96. :underline="false"
  97. icon="el-icon-finished"
  98. @click="addPermit(row, 'change', '变更记录规则')"
  99. >
  100. 变更
  101. </el-link>
  102. <!-- <el-popconfirm
  103. v-if="row.publishStatus == 1"
  104. class="ele-action"
  105. title="确定要撤销此条数据吗?"
  106. @confirm="recordrulesRevokePublish(row)"
  107. >
  108. <template v-slot:reference>
  109. <el-link type="warning" :underline="false" icon="el-icon-delete">
  110. 撤销
  111. </el-link>
  112. </template>
  113. </el-popconfirm> -->
  114. <el-link
  115. v-if="row.publishStatus != 1"
  116. type="primary"
  117. :underline="false"
  118. icon="el-icon-edit"
  119. @click="addPermit(row, 'edit', '修改记录规则')"
  120. >
  121. 修改
  122. </el-link>
  123. <el-popconfirm
  124. v-if="row.publishStatus != 1"
  125. class="ele-action"
  126. title="确定要发布此条数据吗?"
  127. @confirm="publish(row)"
  128. >
  129. <template v-slot:reference>
  130. <el-link
  131. type="primary"
  132. :underline="false"
  133. icon="el-icon-position"
  134. >
  135. 发布
  136. </el-link>
  137. </template>
  138. </el-popconfirm>
  139. <el-link
  140. v-if="row.publishStatus == 1"
  141. type="primary"
  142. :underline="false"
  143. icon="el-icon-tickets"
  144. @click="openHistory(row)"
  145. >
  146. 历史版本
  147. </el-link>
  148. <el-popconfirm
  149. v-if="row.publishStatus != 1"
  150. class="ele-action"
  151. title="确定要删除此条数据吗?"
  152. @confirm="remove(row)"
  153. >
  154. <template v-slot:reference>
  155. <el-link
  156. type="danger"
  157. :underline="false"
  158. icon="el-icon-delete"
  159. >
  160. 删除
  161. </el-link>
  162. </template>
  163. </el-popconfirm>
  164. </template>
  165. <template v-slot:code="{ row }">
  166. <el-link
  167. type="primary"
  168. :underline="false"
  169. @click="addPermit(row, 'detail', '记录规则详情')"
  170. >{{ row.code }}</el-link
  171. >
  172. </template>
  173. </ele-pro-table>
  174. </template>
  175. </ele-split-layout>
  176. </el-card>
  177. <permitAdd
  178. ref="permitAddRef"
  179. @reload="reload"
  180. :typeInfo="treeNode"
  181. @reloadTypeList="getTypeList"
  182. />
  183. <historyModal ref="historyModalRef" />
  184. <editTypeModal
  185. ref="editTypeModalRef"
  186. @reload="getTypeList"
  187. :businessType="
  188. $route.path == '/releaseRules/productionReleaseRules'
  189. ? 1
  190. : '/releaseRules/qualityReleaseRules'
  191. ? 2
  192. : ''
  193. "
  194. ></editTypeModal>
  195. </div>
  196. </template>
  197. <script>
  198. import search from './components/search.vue';
  199. import permitAdd from './components/permitAdd.vue';
  200. import {
  201. recordrulesPage,
  202. recordrulesDeletes,
  203. recordrulesPublish,
  204. recordrulesRevokePublish,
  205. recordrulesTypePage,
  206. recordrulesTypeDeletes
  207. } from '@/api/recordrules/index';
  208. import tabMixins from '@/mixins/tableColumnsMixin';
  209. import dictMixins from '@/mixins/dictMixins';
  210. import historyModal from './components/historyModal.vue';
  211. import editTypeModal from './components/editTypeModal.vue';
  212. export default {
  213. components: { search, permitAdd, historyModal, editTypeModal },
  214. mixins: [tabMixins, dictMixins],
  215. computed: {
  216. columns() {
  217. return [
  218. {
  219. columnKey: 'index',
  220. label: '序号',
  221. type: 'index',
  222. width: 55,
  223. align: 'center',
  224. showOverflowTooltip: true,
  225. fixed: 'left'
  226. },
  227. {
  228. prop: 'code',
  229. label: '编码',
  230. align: 'center',
  231. showOverflowTooltip: true,
  232. minWidth: 110,
  233. slot: 'code'
  234. },
  235. {
  236. prop: 'name',
  237. label: '名称',
  238. align: 'center',
  239. showOverflowTooltip: true,
  240. minWidth: 110
  241. },
  242. {
  243. prop: 'industryType',
  244. label: '行业类别',
  245. align: 'center',
  246. showOverflowTooltip: true,
  247. minWidth: 110,
  248. formatter: (row) => {
  249. return this.getDictValue('行业类别', row.industryType);
  250. }
  251. },
  252. {
  253. prop: 'businessType',
  254. label: '业务分类',
  255. align: 'center',
  256. showOverflowTooltip: true,
  257. minWidth: 110,
  258. formatter: (row) => {
  259. return this.getDictValue('业务类型', row.businessType);
  260. }
  261. },
  262. {
  263. prop: 'reportWorkType',
  264. label: '模块划分',
  265. align: 'center',
  266. showOverflowTooltip: true,
  267. minWidth: 110,
  268. formatter: (row) => {
  269. return this.getDictValue('记录规则报工类型', row.reportWorkType);
  270. }
  271. },
  272. {
  273. prop: 'classify',
  274. label: '应用场景',
  275. align: 'center',
  276. showOverflowTooltip: true,
  277. minWidth: 110,
  278. formatter: (row) => {
  279. return this.getDictValue('记录规则类型', row.classify);
  280. }
  281. },
  282. {
  283. prop: 'recordTemplateStyle',
  284. label: '模板样式',
  285. align: 'center',
  286. showOverflowTooltip: true,
  287. minWidth: 110,
  288. formatter: (row) => {
  289. return this.getDictValue(
  290. '记录表模板样式',
  291. row.recordTemplateStyle
  292. );
  293. }
  294. },
  295. {
  296. prop: 'executeMethodName',
  297. label: '执行方式',
  298. align: 'center',
  299. showOverflowTooltip: true,
  300. minWidth: 110
  301. },
  302. {
  303. prop: 'produceTaskName',
  304. label: '工序名称',
  305. align: 'center',
  306. showOverflowTooltip: true,
  307. minWidth: 110
  308. },
  309. {
  310. prop: 'version',
  311. label: '版本号',
  312. align: 'center',
  313. showOverflowTooltip: true,
  314. minWidth: 110,
  315. formatter: (row) => {
  316. return `${row.versionSymbol}${row.bigVersion}${row.versionMark}${row.smallVersion}`;
  317. }
  318. },
  319. {
  320. prop: 'fromName',
  321. label: '来源版本',
  322. align: 'center',
  323. showOverflowTooltip: true,
  324. minWidth: 150
  325. },
  326. {
  327. prop: 'startDate',
  328. label: '启用日期',
  329. align: 'center',
  330. showOverflowTooltip: true,
  331. minWidth: 110,
  332. formatter: (row) => {
  333. return this.$util.toDateString(row.startDate, 'yyyy-MM-dd');
  334. }
  335. },
  336. {
  337. prop: 'stopDate',
  338. label: '停用日期',
  339. align: 'center',
  340. showOverflowTooltip: true,
  341. minWidth: 110,
  342. formatter: (row) => {
  343. return this.$util.toDateString(row.stopDate, 'yyyy-MM-dd');
  344. }
  345. },
  346. {
  347. prop: '',
  348. label: '周期',
  349. align: 'center',
  350. showOverflowTooltip: true,
  351. formatter: (row) => {
  352. return (
  353. row.frequencyValue +
  354. this.getDictValue('规则周期', row.frequencyUnit)
  355. );
  356. }
  357. },
  358. {
  359. prop: 'createUserName',
  360. label: '创建人',
  361. align: 'center',
  362. showOverflowTooltip: true,
  363. minWidth: 110
  364. },
  365. {
  366. prop: 'createTime',
  367. label: '创建时间',
  368. align: 'center',
  369. showOverflowTooltip: true,
  370. minWidth: 110
  371. },
  372. {
  373. prop: 'enable',
  374. label: '是否启用',
  375. align: 'center',
  376. showOverflowTooltip: true,
  377. minWidth: 110,
  378. formatter: (row) => {
  379. return row.enable ? '启用' : '停用';
  380. }
  381. },
  382. {
  383. prop: 'publishStatus',
  384. label: '状态',
  385. align: 'center',
  386. showOverflowTooltip: true,
  387. minWidth: 110,
  388. formatter: (row) => {
  389. switch (row.publishStatus) {
  390. case 0:
  391. return '草稿';
  392. case 1:
  393. return '已发布';
  394. case 2:
  395. return '已撤销';
  396. default:
  397. return '';
  398. }
  399. }
  400. },
  401. {
  402. columnKey: 'action',
  403. label: '操作',
  404. width: 260,
  405. align: 'center',
  406. resizable: false,
  407. slot: 'action',
  408. fixed: 'right'
  409. }
  410. ];
  411. }
  412. },
  413. data() {
  414. return {
  415. loading: false,
  416. cacheKeyUrl: 'maindata-25922-recordrules-index',
  417. typeTree: [],
  418. treeNode: null,
  419. qmsReportWorkType: [5, 6, 7]
  420. };
  421. },
  422. created() {
  423. this.getTypeList();
  424. },
  425. watch: {
  426. $route() {
  427. this.treeNode = null;
  428. this.getTypeList();
  429. }
  430. },
  431. methods: {
  432. addPermit(row, type, title) {
  433. if (type == 'add') {
  434. if (!this.treeNode) {
  435. return this.$message.warning('请先选择类型节点!');
  436. }
  437. if (this.treeNode.type != 'templateStyle') {
  438. return this.$message.warning('请先选择左侧模板样式节点!');
  439. }
  440. }
  441. this.$refs.permitAddRef.open(row, type, title);
  442. },
  443. async remove(row) {
  444. try {
  445. console.log('row', row);
  446. await recordrulesDeletes([row.id]);
  447. this.$message.success('删除成功');
  448. this.reload();
  449. } catch (error) {
  450. this.$message.error('删除失败');
  451. }
  452. },
  453. /* 表格数据源 */
  454. datasource({ page, limit, where, order }) {
  455. const typeWhere = {};
  456. console.log('this.treeNode~~~~~~~~~~~~~~~', this.treeNode);
  457. if (this.treeNode && this.treeNode.item) {
  458. if (this.treeNode.type === 'industry') {
  459. typeWhere.industryType = this.treeNode.item.industryType;
  460. } else if (this.treeNode.type === 'business') {
  461. typeWhere.industryType = this.treeNode.item.industryType;
  462. typeWhere.businessType = this.treeNode.item.businessType;
  463. } else if (this.treeNode.type === 'reportWork') {
  464. typeWhere.industryType = this.treeNode.item.industryType;
  465. typeWhere.businessType = this.treeNode.item.businessType;
  466. typeWhere.reportWorkType = this.treeNode.item.reportWorkType;
  467. } else if (this.treeNode.type === 'scene') {
  468. typeWhere.industryType = this.treeNode.item.industryType;
  469. typeWhere.businessType = this.treeNode.item.businessType;
  470. typeWhere.reportWorkType = this.treeNode.item.reportWorkType;
  471. typeWhere.classify = this.treeNode.item.sceneType;
  472. } else {
  473. typeWhere.industryType = this.treeNode.item.industryType;
  474. typeWhere.businessType = this.treeNode.item.businessType;
  475. typeWhere.reportWorkType = this.treeNode.item.reportWorkType;
  476. typeWhere.classify = this.treeNode.item.sceneType;
  477. typeWhere.recordTemplateStyle =
  478. this.treeNode.item.templateStyleType;
  479. }
  480. }
  481. return recordrulesPage({
  482. pageNum: page,
  483. size: limit,
  484. ...where,
  485. ...typeWhere,
  486. businessType:
  487. this.$route.path == '/releaseRules/productionReleaseRules' ? 1 : 2
  488. });
  489. },
  490. /* 刷新表格 */
  491. reload(where) {
  492. const typeWhere = {};
  493. if (this.treeNode && this.treeNode.item) {
  494. if (this.treeNode.type === 'industry') {
  495. typeWhere.industryType = this.treeNode.item.industryType;
  496. } else if (this.treeNode.type === 'business') {
  497. typeWhere.industryType = this.treeNode.item.industryType;
  498. typeWhere.businessType = this.treeNode.item.businessType;
  499. } else if (this.treeNode.type === 'reportWork') {
  500. typeWhere.industryType = this.treeNode.item.industryType;
  501. typeWhere.businessType = this.treeNode.item.businessType;
  502. typeWhere.reportWorkType = this.treeNode.item.reportWorkType;
  503. } else if (this.treeNode.type === 'scene') {
  504. typeWhere.industryType = this.treeNode.item.industryType;
  505. typeWhere.businessType = this.treeNode.item.businessType;
  506. typeWhere.reportWorkType = this.treeNode.item.reportWorkType;
  507. typeWhere.classify = this.treeNode.item.sceneType;
  508. } else {
  509. typeWhere.industryType = this.treeNode.item.industryType;
  510. typeWhere.businessType = this.treeNode.item.businessType;
  511. typeWhere.reportWorkType = this.treeNode.item.reportWorkType;
  512. typeWhere.classify = this.treeNode.item.sceneType;
  513. typeWhere.recordTemplateStyle =
  514. this.treeNode.item.templateStyleType;
  515. }
  516. }
  517. this.$refs.table.reload({ page: 1, where, ...typeWhere });
  518. },
  519. // 发布
  520. async publish(row) {
  521. // 判断时间
  522. const startDate = new Date(row.startDate);
  523. const stopDate = new Date(row.stopDate);
  524. if (startDate >= stopDate) {
  525. return this.$message.warning('请先修改规则停用时间!');
  526. }
  527. console.log('row~~~~~', row);
  528. if (!this.qmsReportWorkType.includes(row.reportWorkType)) {
  529. if (row.details.length == 0) {
  530. return this.$message.warning('至少选择一条规则项');
  531. }
  532. if (row.reportWorkType == 4) {
  533. // 生产统计过滤掉非成品统计的明细
  534. row.details = row.details.filter((i) => i.statisticsType);
  535. }
  536. // 判断参数类型是否选择
  537. for (const detail of row.details) {
  538. if (!detail.paramType) {
  539. console.log('detail', detail);
  540. return this.$message.warning('请选择规则明细中的参数类型!');
  541. }
  542. }
  543. }
  544. try {
  545. await recordrulesPublish(row.id);
  546. this.$message.success('发布成功');
  547. this.reload();
  548. } catch (error) {
  549. this.$message.error('发布失败');
  550. }
  551. },
  552. // 撤销
  553. async recordrulesRevokePublish(row) {
  554. try {
  555. await recordrulesRevokePublish(row.id);
  556. this.$message.success('撤销成功');
  557. this.reload();
  558. } catch (error) {
  559. this.$message.error('撤销失败');
  560. }
  561. },
  562. // 历史版本
  563. openHistory(row) {
  564. this.$refs.historyModalRef.open(row.id);
  565. },
  566. // 类型列表
  567. async getTypeList() {
  568. const { list = [] } = await recordrulesTypePage({
  569. pageNum: 1,
  570. size: 99999,
  571. businessType:
  572. this.$route.path == '/releaseRules/productionReleaseRules' ? 1 : 2
  573. });
  574. console.log('type list', list);
  575. const industryMap = new Map();
  576. list.forEach((item) => {
  577. // industry
  578. let industryNode = industryMap.get(item.industryType);
  579. if (!industryNode) {
  580. industryNode = {
  581. id: 'industry-' + item.industryType,
  582. type: 'industry',
  583. value: item.industryType,
  584. label: item.industryName,
  585. children: [],
  586. item,
  587. _businessMap: new Map()
  588. };
  589. industryMap.set(item.industryType, industryNode);
  590. }
  591. // business
  592. let businessNode = industryNode._businessMap.get(item.businessType);
  593. if (!businessNode) {
  594. businessNode = {
  595. id: `business-${item.industryType}-${item.businessType}`,
  596. type: 'business',
  597. value: item.businessType,
  598. label: item.businessName,
  599. children: [],
  600. item,
  601. _reportMap: new Map()
  602. };
  603. industryNode._businessMap.set(item.businessType, businessNode);
  604. industryNode.children.push(businessNode);
  605. }
  606. // reportWork
  607. let reportNode = businessNode._reportMap.get(item.reportWorkType);
  608. if (!reportNode) {
  609. reportNode = {
  610. id: `report-${item.industryType}-${item.businessType}-${item.reportWorkType}`,
  611. type: 'reportWork',
  612. value: item.reportWorkType,
  613. label: item.reportWorkName,
  614. children: [],
  615. item,
  616. _sceneMap: new Map()
  617. };
  618. businessNode._reportMap.set(item.reportWorkType, reportNode);
  619. businessNode.children.push(reportNode);
  620. }
  621. // scene
  622. let sceneNode = reportNode._sceneMap.get(item.sceneType);
  623. if (!sceneNode) {
  624. sceneNode = {
  625. id: `scene-${item.industryType}-${item.businessType}-${item.reportWorkType}-${item.sceneType}`,
  626. type: 'scene',
  627. value: item.sceneType,
  628. label: item.sceneName,
  629. children: [],
  630. item,
  631. _templateMap: new Map()
  632. };
  633. reportNode._sceneMap.set(item.sceneType, sceneNode);
  634. reportNode.children.push(sceneNode);
  635. }
  636. // template style
  637. let templateNode = sceneNode._templateMap.get(item.templateStyleType);
  638. if (!templateNode) {
  639. templateNode = {
  640. id: `template-${item.industryType}-${item.businessType}-${item.reportWorkType}-${item.sceneType}-${item.templateStyleType}`,
  641. type: 'templateStyle',
  642. value: item.templateStyleType,
  643. label: item.templateStyleName,
  644. item,
  645. children: []
  646. };
  647. sceneNode._templateMap.set(item.templateStyleType, templateNode);
  648. sceneNode.children.push(templateNode);
  649. }
  650. // 原始数据
  651. templateNode.item = item;
  652. });
  653. const clean = (node) => {
  654. delete node._businessMap;
  655. delete node._reportMap;
  656. delete node._sceneMap;
  657. delete node._templateMap;
  658. node.children && node.children.forEach(clean);
  659. };
  660. const tree = Array.from(industryMap.values());
  661. tree.forEach(clean);
  662. this.typeTree = tree; // 结果树
  663. console.log('typeTree~~~~~', this.typeTree);
  664. if (!this.treeNode) {
  665. // 默认选中第一个节点
  666. this.treeNode = this.typeTree.length > 0 ? this.typeTree[0] : null;
  667. // console.log('this.treeNode`1111111111111111111111`', this.treeNode);
  668. if (this.treeNode) {
  669. // 默认选中节点
  670. this.$nextTick(() => {
  671. this.$refs.treeRef.setCurrentKey(this.treeNode.id);
  672. this.reload();
  673. });
  674. }
  675. }
  676. console.log('typeTree', this.typeTree);
  677. },
  678. // 打开新增类型
  679. openEditType(type = 'add', data = null) {
  680. this.$refs.editTypeModalRef.open(
  681. type,
  682. type == 'add' ? this.treeNode : data
  683. );
  684. },
  685. async deleteTypeNode(node) {
  686. console.log('delete node', node);
  687. // 删除类型节点
  688. await recordrulesTypeDeletes([node.data.item.id]);
  689. this.$message.success('删除成功');
  690. this.getTypeList();
  691. },
  692. handleNodeClick(data) {
  693. console.log('data', data);
  694. this.treeNode = data;
  695. this.reload();
  696. }
  697. }
  698. };
  699. </script>
  700. <style lang="scss" scoped>
  701. .sys-organization-list {
  702. height: calc(100vh - 264px);
  703. box-sizing: border-box;
  704. border-width: 1px;
  705. border-style: solid;
  706. overflow: auto;
  707. padding: 10px 5px;
  708. }
  709. .sys-organization-list :deep(.el-tree-node__content) {
  710. height: 30px;
  711. & > .el-tree-node__expand-icon {
  712. // margin-left: 10px;
  713. }
  714. }
  715. .custom-tree-node {
  716. flex: 1;
  717. display: flex;
  718. align-items: center;
  719. justify-content: space-between;
  720. font-size: 14px;
  721. padding-right: 8px;
  722. .edit-box {
  723. display: none;
  724. gap: 2px;
  725. cursor: pointer;
  726. }
  727. &:hover .edit-box {
  728. display: flex;
  729. }
  730. }
  731. </style>