mtx-process-catalog-tree.vue 19 KB

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