law-case-catalog-tree.vue 13 KB

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