audit-operate-tree.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  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-tree-select
  11. v-if="isSelectDep"
  12. v-model="depvalue"
  13. :allow-clear="true"
  14. :tree-data="depOptions"
  15. @change="changedep"
  16. >
  17. </a-tree-select>
  18. <!-- 搜索框 -->
  19. <a-input-search
  20. v-if="isSearch"
  21. v-model="searchStr"
  22. placeholder=""
  23. allow-clear
  24. @change="onSearchChange"
  25. />
  26. <a-spin :spinning="spinning" :class="$style.spin" />
  27. <a-empty v-if="empty" />
  28. <a-tree
  29. v-else
  30. :key="refreshKey"
  31. ref="auditTree"
  32. v-model="checkedKeys"
  33. :block-node="false"
  34. :show-icon="true"
  35. :show-line="showLine"
  36. :check-strictly="true"
  37. :checkable="checkable"
  38. :draggable="draggable"
  39. :tree-data="treeData"
  40. :replace-fields="replaceFields"
  41. :selected-keys="defaultSelectedKeys"
  42. :expanded-keys="expandedKeys"
  43. :default-expanded-keys="defaultTreeExpandedKeys"
  44. :load-data="onLoadData"
  45. @select="treeSelect"
  46. @check="treeCheck"
  47. @expand="onExpand"
  48. @dragenter="onDragEnter"
  49. @drop="onDrop"
  50. >
  51. <template v-slot:title="{ text, props }">
  52. <span v-if="text.indexOf(searchValue) > -1">
  53. {{ text.substr(0, text.indexOf(searchValue))
  54. }}<span :class="$style.active">{{ searchValue }}</span
  55. >{{ text.substr(text.indexOf(searchValue) + searchValue.length) }}</span
  56. ><span v-else>{{ text }}</span>
  57. <!-- 后台返回数量,如果有数量的话就显示 -->
  58. <span v-if="props.count > 0">({{ props.count }})</span>
  59. </template>
  60. <!-- 如果已配置负责人,则展示用户图标 -->
  61. <template slot="hasuser" slot-scope="item">
  62. <a-icon type="team" @click="showUser(item)" />
  63. </template>
  64. </a-tree>
  65. <a-button v-if="showPrimary" type="primary" :class="$style.fold" @click="foldClick">
  66. <a-icon :type="icontype" />
  67. </a-button>
  68. </a-card>
  69. </template>
  70. <script>
  71. import { getUserInfo } from '@/common/store-mixin'
  72. import AuditPermissionTreeMixins from './audit-permission-tree-mixins'
  73. import auditOperateTreeService from './audit-operate-tree-service'
  74. import components from './_import-components/audit-operate-tree-import'
  75. export default {
  76. name: 'AuditOperateTree',
  77. metaInfo: {
  78. title: 'AuditOperateTree',
  79. },
  80. components,
  81. mixins: [AuditPermissionTreeMixins],
  82. props: {
  83. // 默认选中节点
  84. // selectedKeys: {
  85. // type: Array,
  86. // default: function() {
  87. // return [1]
  88. // },
  89. // },
  90. // 是否开启搜索
  91. isSearch: {
  92. type: Boolean,
  93. default: false,
  94. },
  95. // 是否显示复选框
  96. checkable: {
  97. type: Boolean,
  98. default: false,
  99. },
  100. // 是否可以拖拽
  101. draggable: {
  102. type: Boolean,
  103. default: false,
  104. },
  105. // 是否显示连线
  106. showLine: {
  107. type: Boolean,
  108. default: false,
  109. },
  110. // 根节点名称
  111. topNodeText: {
  112. type: String,
  113. default: 'XX科技1',
  114. },
  115. // 地址树接口数据源
  116. treeparams: {
  117. type: Object,
  118. default: () => {
  119. return {}
  120. },
  121. },
  122. // 默认展开节点id
  123. defaultExpandedKeys: {
  124. type: Array,
  125. default: () => {
  126. return ['0']
  127. },
  128. },
  129. // 是否显示部门选择下拉框
  130. isSelectDep: {
  131. type: Boolean,
  132. default: false,
  133. },
  134. // 部门下拉框列表值
  135. depOptions: {
  136. type: Array,
  137. default: () => {
  138. return []
  139. },
  140. },
  141. // 部门下拉框默认值
  142. defaultDepValue: {
  143. type: Array,
  144. default: () => {
  145. return []
  146. },
  147. },
  148. // 是否显示隐藏按钮
  149. showPrimary: {
  150. type: Boolean,
  151. default: true,
  152. },
  153. // 地址树是否单选
  154. single: {
  155. type: Boolean,
  156. default: false,
  157. },
  158. refreshKey: {
  159. type: Number,
  160. default: 0,
  161. },
  162. },
  163. data() {
  164. return {
  165. defaultTreeExpandedKeys: ['0'],
  166. defaultSelectedKeys: ['0'],
  167. selectedKeys: ['0'],
  168. depvalue: [], // 选择的组织数据
  169. checkedKeys: [], // 选中的节点数据
  170. treeData: [],
  171. dataList: [], // 数组dataList,搜索要用
  172. spinning: true,
  173. empty: false,
  174. icontype: 'left',
  175. fold: false,
  176. replaceFields: {
  177. title: 'text',
  178. key: 'id',
  179. },
  180. expandedKeys: ['0'],
  181. backupsExpandedKeys: [],
  182. autoExpandParent: false,
  183. searchValue: '',
  184. searchStr: '',
  185. defaultTopNodeText: '审计框架',
  186. defaultTopNodeId: '0',
  187. key: 1,
  188. treedata: [],
  189. }
  190. },
  191. created() {},
  192. methods: {
  193. // 小箭头点击事件
  194. foldClick() {
  195. this.fold = !this.fold
  196. if (this.fold) {
  197. this.icontype = 'right'
  198. } else {
  199. this.icontype = 'left'
  200. }
  201. },
  202. onDragEnter(info) {
  203. // expandedKeys 需要受控时设置
  204. this.expandedKeys = info.expandedKeys
  205. },
  206. // 拖拽节点方法
  207. onDrop(info) {
  208. // 被插入节点信息
  209. const dropKey = info.node.eventKey
  210. // 拖拽节点信息
  211. const dragKey = info.dragNode.eventKey
  212. const dropPos = info.node.pos.split('-')
  213. // -1表示之前 0表示合并 1表示之后
  214. const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1])
  215. const loop = (data, key, callback) => {
  216. data.forEach((item, index, arr) => {
  217. if (item.id === key) {
  218. return callback(item, index, arr)
  219. }
  220. if (item.children) {
  221. return loop(item.children, key, callback)
  222. }
  223. })
  224. }
  225. const data = this.treeData
  226. // Find dragObject
  227. let dragObj
  228. loop(data, dragKey, (item, index, arr) => {
  229. arr.splice(index, 1)
  230. dragObj = item
  231. })
  232. if (!info.dropToGap) {
  233. // Drop on the content
  234. loop(data, dropKey, (item) => {
  235. item.children = item.children || []
  236. // where to insert 示例添加到尾部,可以是随意位置
  237. item.children.push(dragObj)
  238. })
  239. } else if (
  240. (info.node.children || []).length > 0 && // Has children
  241. info.node.expanded && // Is expanded
  242. dropPosition === 1 // On the bottom gap
  243. ) {
  244. loop(data, dropKey, (item) => {
  245. item.children = item.children || []
  246. // where to insert 示例添加到尾部,可以是随意位置
  247. item.children.unshift(dragObj)
  248. })
  249. } else {
  250. let ar
  251. let i
  252. loop(data, dropKey, (item, index, arr) => {
  253. ar = arr
  254. i = index
  255. })
  256. if (dropPosition === -1) {
  257. ar.splice(i, 0, dragObj)
  258. } else {
  259. ar.splice(i + 1, 0, dragObj)
  260. }
  261. }
  262. this.treeData = data
  263. // 拖拽完成,调用接口
  264. const dragParams = {
  265. configId: this.treeparams.configId,
  266. id: dragKey,
  267. targetId: dropKey,
  268. position: dropPosition,
  269. }
  270. auditOperateTreeService.dragNode(dragParams).then((res) => {
  271. console.log(res)
  272. })
  273. },
  274. // 点击地址树展开时调用
  275. onLoadData(treeNode) {
  276. const userInfo = getUserInfo()
  277. // return auditOperateTreeService.getYearTree(userInfo.account).then((res) => {
  278. // treeNode.dataRef.children = res.data
  279. // this.treeData = this.transformData([...this.treeData])
  280. // this.generateList(this.treeData)
  281. // })
  282. },
  283. // 初始化地址树
  284. initTreeData(depId) {
  285. let topDepId = null
  286. const userInfo = getUserInfo()
  287. auditOperateTreeService.getYearTree(userInfo.account).then((res) => {
  288. topDepId = !depId ? 1 : depId
  289. console.log(res.data)
  290. this.treedata = res.data
  291. // this.defaultTopNodeId = res.data[0].id
  292. // this.defaultTopNodeText = res.data[0].text
  293. // this.defaultTreeExpandedKeys = [res.data[0].id]
  294. // this.defaultSelectedKeys = [res.data[0].id]
  295. // this.selectedKeys = [res.data[0].id]
  296. // node.dataRef.id
  297. // this.treeSelect([res.data[0].id], {
  298. // node: { dataRef: { id: res.data[0].id, text: res.data[0].text } },
  299. // })
  300. // auditOperateTreeService.getCategoryTree(topDepId).then((res) => {
  301. this.spinning = false
  302. const treeNode = [
  303. {
  304. id: this.defaultTopNodeId,
  305. text: this.defaultTopNodeText,
  306. leaf: false,
  307. props: {},
  308. children: res.data,
  309. key: this.defaultTopNodeId,
  310. },
  311. ]
  312. this.treeData = this.transformData(treeNode)
  313. this.expandedKeys = this.defaultTreeExpandedKeys
  314. this.generateList(this.treeData)
  315. this.empty = false
  316. })
  317. },
  318. },
  319. }
  320. </script>
  321. <style module lang="scss">
  322. @use '@/common/design' as *;
  323. .spin {
  324. width: 100%;
  325. line-height: 30;
  326. }
  327. .ywlxtree {
  328. :global(.ant-tree-title) {
  329. display: inline-block;
  330. width: 100%;
  331. overflow: hidden;
  332. text-overflow: ellipsis;
  333. white-space: nowrap;
  334. }
  335. :global(.ant-input-search) {
  336. margin: 8px 0;
  337. overflow: hidden;
  338. }
  339. .active {
  340. color: $primary-color;
  341. }
  342. :global(.ant-select) {
  343. width: 100%;
  344. overflow: hidden;
  345. }
  346. }
  347. .treewrap {
  348. position: relative;
  349. display: flex;
  350. flex-direction: column;
  351. width: 35%;
  352. min-height: 100%;
  353. margin-right: $padding-lg;
  354. transition: width 0.2s;
  355. .fold {
  356. position: absolute;
  357. top: calc(50% - 30px);
  358. right: -15px;
  359. z-index: 2;
  360. width: 15px;
  361. height: 75px;
  362. padding: 0;
  363. border-radius: 0 10px 10px 0;
  364. }
  365. :global(.ant-tree) {
  366. overflow: hidden;
  367. text-overflow: ellipsis;
  368. white-space: nowrap;
  369. }
  370. :global(.ant-card-body) {
  371. background: $white;
  372. }
  373. }
  374. .collapse {
  375. width: 0 !important;
  376. :global(.ant-card-body) {
  377. background: transparent;
  378. :global(.ant-empty) {
  379. display: none;
  380. }
  381. }
  382. }
  383. // 单选样式
  384. .single {
  385. :global .ant-tree-checkbox {
  386. .ant-tree-checkbox-inner {
  387. border-radius: 100px;
  388. &::after {
  389. position: absolute;
  390. top: 3px;
  391. left: 3px;
  392. display: table;
  393. width: 8px;
  394. height: 8px;
  395. content: ' ';
  396. background-color: $primary-color;
  397. border: 0;
  398. border-radius: 8px;
  399. opacity: 0;
  400. transition: all 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
  401. transform: scale(0);
  402. }
  403. }
  404. &.ant-tree-checkbox-checked {
  405. &::after {
  406. position: absolute;
  407. top: 16.67%;
  408. left: 0;
  409. width: 100%;
  410. height: 66.67%;
  411. content: '';
  412. border: 1px solid #1890ff;
  413. border-radius: 50%;
  414. animation: antRadioEffect 0.36s ease-in-out;
  415. animation-fill-mode: both;
  416. }
  417. .ant-tree-checkbox-inner {
  418. background-color: $white;
  419. &::after {
  420. opacity: 1;
  421. transition: all 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
  422. transform: scale(1);
  423. }
  424. }
  425. }
  426. }
  427. }
  428. </style>