audit-maintain-dept-tree.vue 17 KB

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