audit-maintain-catalog-tree.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. <template>
  2. <a-card
  3. :class="[
  4. $style.treewrap,
  5. $style.ywlxtree,
  6. { [$style.collapse]: fold, [$style.single]: single },
  7. ]"
  8. >
  9. <!-- 选择部门 -->
  10. <!-- <a-select v-show="isSelectDep" v-model="depvalue" :options="depOptions" @change="changedep">
  11. </a-select> -->
  12. <a-form-model>
  13. <a-form-model-item :label="null" prop="orgValue">
  14. <AuditGroupPicker
  15. ref="unitNames"
  16. v-model="orgValue"
  17. :single="true"
  18. :read-only="false"
  19. :selectall="true"
  20. :top-node-text="'审计机构库'"
  21. :treeparams="{
  22. formId: 'IAM_ORG',
  23. idColumnId: 'id',
  24. nameColumnId: 'org_name',
  25. parentColumnId: 'parent_id',
  26. otherColumnId: ['org_id', 'creation_time'],
  27. configId: '11',
  28. type: 'SJ',
  29. }"
  30. @change="changedep"
  31. />
  32. </a-form-model-item>
  33. </a-form-model>
  34. <a-spin :spinning="spinning" :class="$style.spin" />
  35. <a-empty v-if="empty" />
  36. <a-tree
  37. v-else
  38. :key="key"
  39. ref="auditMaintainCatalogTree"
  40. v-model="checkedKeys"
  41. :block-node="false"
  42. :show-icon="true"
  43. :show-line="showLine"
  44. :check-strictly="true"
  45. :checkable="false"
  46. :draggable="draggable"
  47. :tree-data="treeData"
  48. :replace-fields="replaceFields"
  49. :selected-keys="defaultSelectedKeys"
  50. :expanded-keys="expandedKeys"
  51. :default-expanded-keys="defaultTreeExpandedKeys"
  52. :load-data="onLoadData"
  53. @select="treeSelect"
  54. @check="treeCheck"
  55. @expand="onExpand"
  56. @drop="onDrop"
  57. >
  58. </a-tree>
  59. </a-card>
  60. </template>
  61. <script>
  62. import { Message } from 'ant-design-vue'
  63. import { getUserInfo } from '@/common/store-mixin'
  64. import AuditGroupPicker from '../../components/picker/audit-group-picker.vue'
  65. import auditMaintainService from './audit-maintain-service'
  66. import components from './_import-components/audit-maintain-catalog-tree-import'
  67. export default {
  68. name: 'AuditMaintainCatalogTree',
  69. metaInfo: {
  70. title: 'AuditMaintainCatalogTree',
  71. },
  72. components: {
  73. ...components,
  74. AuditGroupPicker,
  75. },
  76. props: {
  77. // 默认选中节点
  78. selectedKeys: {
  79. type: Array,
  80. default: function() {
  81. return []
  82. },
  83. },
  84. // 查看模式 manager维护权限 read查看权限
  85. managerType: {
  86. type: String,
  87. default: 'manager',
  88. },
  89. // 是否显示复选框
  90. checkable: {
  91. type: Boolean,
  92. default: true,
  93. },
  94. // 是否可以拖拽
  95. draggable: {
  96. type: Boolean,
  97. default: false,
  98. },
  99. // 是否显示连线
  100. showLine: {
  101. type: Boolean,
  102. default: false,
  103. },
  104. // 根节点名称
  105. topNodeText: {
  106. type: String,
  107. default: '根节点',
  108. },
  109. // 地址树接口数据源
  110. treeparams: {
  111. type: Object,
  112. default: () => {
  113. return {}
  114. },
  115. },
  116. // 默认展开节点id
  117. defaultExpandedKeys: {
  118. type: Array,
  119. default: () => {
  120. return ['0']
  121. },
  122. },
  123. // 是否显示隐藏按钮
  124. showPrimary: {
  125. type: Boolean,
  126. default: true,
  127. },
  128. // 地址树是否单选
  129. single: {
  130. type: Boolean,
  131. default: false,
  132. },
  133. refreshKey: {
  134. type: Number,
  135. default: 0,
  136. },
  137. /**
  138. * 是否展示部门下拉列表
  139. */
  140. isSelectDep: {
  141. type: Boolean,
  142. default: false,
  143. },
  144. cate: {
  145. type: Boolean,
  146. default: false,
  147. },
  148. },
  149. data() {
  150. return {
  151. treeData: [],
  152. defaultTreeExpandedKeys: ['0'],
  153. defaultSelectedKeys: ['0'],
  154. checkedKeys: [], // 选中的节点数据
  155. dataList: [], // 数组dataList,搜索要用
  156. spinning: true,
  157. empty: false,
  158. icontype: 'left',
  159. fold: false,
  160. replaceFields: {
  161. title: 'text',
  162. key: 'id',
  163. },
  164. expandedKeys: ['0'],
  165. backupsExpandedKeys: [],
  166. autoExpandParent: false,
  167. defaultTopNodeText: '审计事项分类',
  168. defaultTopNodeId: '0',
  169. key: 0,
  170. newtree: [],
  171. depOptions: [], // 部门下拉框列表值
  172. edit: true, // 当前节点是否可编辑
  173. depvalue: '', // 选择的组织数据
  174. orgValue: '',
  175. orgId: null,
  176. }
  177. },
  178. created() {
  179. // 如果有默认展开节点,则赋值
  180. if (this.defaultExpandedKeys) {
  181. this.defaultTreeExpandedKeys = [...this.defaultExpandedKeys]
  182. }
  183. // 如果有传根节点名称,则赋值
  184. if (this.topNodeText) {
  185. this.defaultTopNodeText = this.topNodeText
  186. }
  187. // 如果有传默认选中节点,则赋值
  188. if (this.selectedKeys.length > 0) {
  189. this.defaultSelectedKeys = [...this.selectedKeys]
  190. }
  191. // this.initTreeData()
  192. this.initDeptList('iamModelCategory')
  193. },
  194. methods: {
  195. // 刷新树节点
  196. refreshNode(nodeId) {
  197. this.key++
  198. this.parentId = nodeId
  199. this.refreshData(this.treeData[0])
  200. },
  201. // 刷新树节点
  202. refreshData(treeData) {
  203. if (treeData.id + '' === this.parentId + '') {
  204. return auditMaintainService
  205. .findIamAuditMaintainCategoryTree(treeData.id, treeData.id)
  206. .then((res) => {
  207. treeData.children = res.data
  208. if (res.data.length > 0) {
  209. treeData.isLeaf = false
  210. if (this.expandedKeys.indexOf(treeData.id) === -1) {
  211. this.expandedKeys = new Set(this.expandedKeys.push(treeData.id))
  212. }
  213. } else {
  214. treeData.isLeaf = true
  215. }
  216. return false
  217. })
  218. }
  219. if (treeData.children) {
  220. treeData.children.forEach((item) => {
  221. this.refreshData(item)
  222. })
  223. }
  224. },
  225. // 初始化部门下拉 分级授权获取部门下拉列表 维护权限
  226. initDeptList(formId) {
  227. auditMaintainService.getManagedHierarchyOrg(formId).then((res) => {
  228. res.data.editNodes.forEach((item) => {
  229. this.depOptions.push({
  230. label: item.text,
  231. value: item.id,
  232. text: item.text,
  233. edit: true,
  234. })
  235. })
  236. if (this.managerType !== 'manager') {
  237. // 取编辑和查看节点并集
  238. res.data.viewNodes.forEach((item, index) => {
  239. const node = this.depOptions.find((nodeinfo) => {
  240. return nodeinfo.value === item.id
  241. })
  242. if (!node) {
  243. this.depOptions.push({
  244. label: item.text,
  245. value: item.id,
  246. text: item.text,
  247. edit: false,
  248. })
  249. }
  250. })
  251. }
  252. // 部门下拉框列表值赋值
  253. if (this.depOptions.length > 0) {
  254. this.depvalue = this.depOptions[0].value
  255. // 加载默认值
  256. this.defaultTopNodeId = 'o_' + this.depOptions[0].value
  257. this.defaultTopNodeText = this.depOptions[0].text + this.topNodeText
  258. this.defaultTreeExpandedKeys = ['o_' + this.depOptions[0].value]
  259. this.defaultSelectedKeys = ['o_' + this.depOptions[0].value]
  260. this.edit = this.depOptions[0].edit
  261. this.initTreeData(this.depOptions[0].value)
  262. this.$emit('depChanged', this.depOptions[0].value, this.depOptions[0])
  263. this.$emit('treeSelect', [this.depOptions[0].value], {
  264. selectedNodes: [
  265. {
  266. data: {
  267. props: {
  268. id: this.depOptions[0].value,
  269. text: this.depOptions[0].text + this.topNodeText,
  270. edit: this.depOptions[0].edit,
  271. },
  272. },
  273. },
  274. ],
  275. node: {
  276. dataRef: {
  277. props: {
  278. isroot: true,
  279. },
  280. },
  281. },
  282. })
  283. } else {
  284. this.defaultTopNodeId = null
  285. this.defaultTopNodeText = null
  286. this.defaultTreeExpandedKeys = []
  287. this.defaultSelectedKeys = []
  288. this.$emit('treeSelect', [9999999], {
  289. selectedNodes: [],
  290. })
  291. this.spinning = false
  292. this.empty = true
  293. }
  294. })
  295. },
  296. /***
  297. * 选中复选框时的事件
  298. */
  299. treeCheck(allKeys, echecked) {
  300. // echecked.checkedNodes 选中的所有节点信息为数组 .data.props 获取详细信息
  301. // echecked.node.dataRef 当前选中的节点信息
  302. if (this.single) {
  303. this.checkedKeys.checked = []
  304. this.checkedKeys.checked = [echecked.node.dataRef.id]
  305. }
  306. this.$emit('checkedKeys', echecked.node.dataRef, echecked.checkedNodes)
  307. },
  308. // 点击树
  309. treeSelect(selectedKeys, info) {
  310. this.defaultSelectedKeys = selectedKeys
  311. this.$emit('treeSelect', selectedKeys, info)
  312. },
  313. transformData(data) {
  314. return data.map((d) => {
  315. const { children, ...rest } = d
  316. return {
  317. ...rest,
  318. children: children && this.transformData(children),
  319. scopedSlots: { title: 'title' },
  320. }
  321. })
  322. },
  323. onExpand(expandedKeys) {
  324. // 用户点击展开时,取消自动展开效果
  325. this.expandedKeys = expandedKeys
  326. this.autoExpandParent = false
  327. },
  328. // 点击地址树展开时调用
  329. onLoadData(treeNode) {
  330. let parentId
  331. if (treeNode.dataRef.props.isroot) {
  332. parentId = -1
  333. } else {
  334. parentId = parseInt(treeNode.dataRef.id)
  335. }
  336. return auditMaintainService
  337. .findIamAuditMaintainCategoryTree(parentId, this.depvalue)
  338. .then((res) => {
  339. treeNode.dataRef.children = res.data
  340. // this.setDisabless(res.data)
  341. this.treeData = this.transformData([...this.treeData])
  342. })
  343. },
  344. // 添加是否可点击属性
  345. setDisabless(data) {
  346. return data.map((d) => {
  347. const props = d.props
  348. if (props && props.isEnd === '1') {
  349. // 置灰不可选
  350. d.disabled = true
  351. }
  352. })
  353. },
  354. // 初始化地址树
  355. initTreeData(depId) {
  356. const topDepId = depId === null || depId === undefined ? 0 : depId
  357. auditMaintainService.findIamAuditMaintainCategoryTree(topDepId, topDepId).then((res) => {
  358. // if (res.data) {
  359. // this.defaultSelectedKeys = [res.data[0].id]
  360. // }
  361. // this.mmInitTreeData(res)
  362. this.spinning = false
  363. const treeNode = [
  364. {
  365. id: this.defaultTopNodeId,
  366. text: this.defaultTopNodeText,
  367. isLeaf: false,
  368. props: {
  369. isroot: true,
  370. },
  371. children: [],
  372. key: this.defaultTopNodeId,
  373. },
  374. ]
  375. if (res.data.length) {
  376. treeNode.children = res.data
  377. }
  378. this.treeData = this.transformData(treeNode)
  379. this.expandedKeys = this.defaultTreeExpandedKeys
  380. this.generateList(this.treeData)
  381. this.empty = false
  382. })
  383. },
  384. // 处理搜索用的dataList
  385. generateList(data) {
  386. for (let i = 0; i < data.length; i++) {
  387. const node = data[i]
  388. const key = node.id
  389. const title = node.text
  390. const props = node.props
  391. // 用来判断此几点是否已经配置了用户,需要后台返回标识位进行判断
  392. if (props.showuser) {
  393. data[i].scopedSlots.icon = 'hasuser'
  394. }
  395. this.dataList.push({ key, id: key, title: title, props })
  396. if (node.children) {
  397. this.generateList(node.children)
  398. }
  399. }
  400. },
  401. mmInitTreeData(res) {
  402. this.spinning = false
  403. if (res.data.length) {
  404. this.treeData = this.transformData(res.data)
  405. this.expandedKeys = this.defaultTreeExpandedKeys
  406. this.empty = false
  407. } else {
  408. this.empty = true
  409. }
  410. },
  411. // 刷新树方法
  412. refreshTree() {
  413. this.key++
  414. },
  415. // 组织下拉框change时触发
  416. changedep(value) {
  417. this.key++
  418. if (value.length === 0) {
  419. this.depvalue = 0
  420. this.defaultTreeExpandedKeys = ['0']
  421. this.defaultSelectedKeys = ['0']
  422. this.defaultTopNodeId = '0'
  423. this.defaultTopNodeText = this.topNodeText
  424. } else {
  425. this.depvalue = value[0].oldId
  426. this.defaultTreeExpandedKeys = ['o_' + value[0].oldId]
  427. this.defaultSelectedKeys = ['o_' + value[0].oldId]
  428. this.defaultTopNodeId = 'o_' + value[0].oldId
  429. this.defaultTopNodeText = value[0].text + this.topNodeText
  430. this.edit = true
  431. }
  432. this.initTreeData(this.depvalue)
  433. this.$emit('depChanged', this.depvalue, value[0])
  434. this.$emit(
  435. 'treeSelect',
  436. { id: value[0].oldId },
  437. {
  438. selectedNodes: [
  439. {
  440. data: {
  441. props: {
  442. id: this.defaultTopNodeId,
  443. text: value[0].text + this.topNodeText,
  444. edit: true,
  445. },
  446. },
  447. },
  448. ],
  449. node: {
  450. dataRef: {
  451. props: {
  452. isroot: true,
  453. },
  454. },
  455. },
  456. }
  457. )
  458. },
  459. // 拖拽节点方法
  460. onDrop(info) {
  461. // 被插入节点信息
  462. const dropKey = info.node.eventKey
  463. const dropNode = info.node.dataRef
  464. // 拖拽节点信息
  465. const dragKey = info.dragNode.eventKey
  466. const dragNode = info.dragNode.dataRef
  467. const dropPos = info.node.pos.split('-')
  468. // -1表示之前 0表示合并 1表示之后
  469. const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1])
  470. // 分类合并至事项内
  471. if (dropNode.props.isroot && (dropPosition === 1) | (dropPosition === -1)) {
  472. Message.info('不能将分类拖拽至与根节点同层级', 1)
  473. return false
  474. }
  475. const loop = (data, key, callback) => {
  476. data.forEach((item, index, arr) => {
  477. if (item.id === key) {
  478. return callback(item, index, arr)
  479. }
  480. if (item.children) {
  481. return loop(item.children, key, callback)
  482. }
  483. })
  484. }
  485. const data = this.treeData
  486. // Find dragObject
  487. let dragObj
  488. loop(data, dragKey, (item, index, arr) => {
  489. arr.splice(index, 1)
  490. dragObj = item
  491. })
  492. if (!info.dropToGap) {
  493. // Drop on the content
  494. loop(data, dropKey, (item) => {
  495. item.children = item.children || []
  496. // where to insert 示例添加到尾部,可以是随意位置
  497. item.children.push(dragObj)
  498. })
  499. } else if (
  500. (info.node.children || []).length > 0 && // Has children
  501. info.node.expanded && // Is expanded
  502. dropPosition === 1 // On the bottom gap
  503. ) {
  504. loop(data, dropKey, (item) => {
  505. item.children = item.children || []
  506. // where to insert 示例添加到尾部,可以是随意位置
  507. item.children.unshift(dragObj)
  508. })
  509. } else {
  510. let ar
  511. let i
  512. loop(data, dropKey, (item, index, arr) => {
  513. ar = arr
  514. i = index
  515. })
  516. if (dropPosition === -1) {
  517. ar.splice(i, 0, dragObj)
  518. } else {
  519. ar.splice(i + 1, 0, dragObj)
  520. }
  521. }
  522. this.treeData = data
  523. // 拖拽完成,调用接口
  524. const dragParams = {
  525. nodeId: dragNode.id,
  526. targetnodeType: dropNode.props.isroot ? 'root' : 'category', // category/root
  527. targetnodeId: dropNode.id,
  528. position: dropPosition, // -1 0 1 之前 合并 之后
  529. }
  530. auditMaintainService.dragNode(dragParams).then((res) => {
  531. if (!res) {
  532. Message.info(dropNode.text + '节点下存在相同分类编号,拖拽失败')
  533. }
  534. })
  535. },
  536. },
  537. }
  538. </script>
  539. <style module lang="scss">
  540. @use '@/common/design' as *;
  541. .spin {
  542. width: 100%;
  543. line-height: 30;
  544. }
  545. .ywlxtree {
  546. :global(.ant-tree-title) {
  547. display: inline-block;
  548. width: 100%;
  549. overflow: hidden;
  550. text-overflow: ellipsis;
  551. white-space: nowrap;
  552. }
  553. :global(.ant-input-search) {
  554. margin: 8px 0;
  555. overflow: hidden;
  556. }
  557. .active {
  558. color: $primary-color;
  559. }
  560. :global(.ant-select) {
  561. width: 100%;
  562. overflow: hidden;
  563. }
  564. }
  565. .treewrap {
  566. position: relative;
  567. display: flex;
  568. flex-direction: column;
  569. width: 20%;
  570. min-height: 100%;
  571. margin-right: $padding-lg;
  572. transition: width 0.2s;
  573. .fold {
  574. position: absolute;
  575. top: calc(50% - 30px);
  576. right: -15px;
  577. z-index: 2;
  578. width: 15px;
  579. height: 75px;
  580. padding: 0;
  581. border-radius: 0 10px 10px 0;
  582. }
  583. :global(.ant-tree) {
  584. overflow: hidden;
  585. text-overflow: ellipsis;
  586. white-space: nowrap;
  587. }
  588. :global(.ant-card-body) {
  589. background: $white;
  590. }
  591. }
  592. .collapse {
  593. width: 0;
  594. :global(.ant-card-body) {
  595. background: transparent;
  596. :global(.ant-empty) {
  597. display: none;
  598. }
  599. }
  600. }
  601. // 单选样式
  602. .single {
  603. :global .ant-tree-checkbox {
  604. .ant-tree-checkbox-inner {
  605. border-radius: 100px;
  606. &::after {
  607. position: absolute;
  608. top: 3px;
  609. left: 3px;
  610. display: table;
  611. width: 8px;
  612. height: 8px;
  613. content: ' ';
  614. background-color: $primary-color;
  615. border: 0;
  616. border-radius: 8px;
  617. opacity: 0;
  618. transition: all 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
  619. transform: scale(0);
  620. }
  621. }
  622. &.ant-tree-checkbox-checked {
  623. &::after {
  624. position: absolute;
  625. top: 16.67%;
  626. left: 0;
  627. width: 100%;
  628. height: 66.67%;
  629. content: '';
  630. border: 1px solid #1890ff;
  631. border-radius: 50%;
  632. animation: antRadioEffect 0.36s ease-in-out;
  633. animation-fill-mode: both;
  634. }
  635. .ant-tree-checkbox-inner {
  636. background-color: $white;
  637. &::after {
  638. opacity: 1;
  639. transition: all 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
  640. transform: scale(1);
  641. }
  642. }
  643. }
  644. }
  645. }
  646. </style>