index.vue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. <template>
  2. <div>
  3. <div
  4. v-show="isShowSelect"
  5. class="mask"
  6. @click="isShowSelect = !isShowSelect" />
  7. <el-popover
  8. v-model="isShowSelect"
  9. placement="bottom-start"
  10. :width="width"
  11. trigger="manual"
  12. style="padding: 12px 0"
  13. @hide="popoverHide">
  14. <el-tree
  15. ref="tree"
  16. class="common-tree"
  17. :style="style"
  18. :data="data"
  19. :props="defaultProps"
  20. :show-checkbox="multiple"
  21. :node-key="nodeKey"
  22. :check-strictly="checkStrictly"
  23. :expand-on-click-node="true"
  24. :check-on-click-node="multiple"
  25. :highlight-current="true"
  26. @node-click="handleNodeClick"
  27. @check-change="handleCheckChange" />
  28. <el-select
  29. slot="reference"
  30. ref="select"
  31. v-model="selectedData"
  32. :style="selectStyle"
  33. :size="size"
  34. :multiple="multiple"
  35. :clearable="clearable"
  36. :collapse-tags="collapseTags"
  37. class="tree-select"
  38. @click.native="isShowSelect = !isShowSelect"
  39. @remove-tag="removeSelectedNodes"
  40. @clear="removeSelectedNode"
  41. @change="changeSelectedNodes">
  42. <el-option
  43. v-for="item in options"
  44. :key="item.value"
  45. :label="item.label"
  46. :value="item.value" />
  47. </el-select>
  48. </el-popover>
  49. </div>
  50. </template>
  51. <script>
  52. export default {
  53. props: {
  54. // 树结构数据
  55. data: {
  56. type: Array,
  57. default() {
  58. return []
  59. },
  60. },
  61. defaultProps: {
  62. type: Object,
  63. default() {
  64. return {
  65. children: 'children',
  66. label: 'name',
  67. }
  68. },
  69. },
  70. // 配置是否可多选
  71. multiple: {
  72. type: Boolean,
  73. default() {
  74. return false
  75. },
  76. },
  77. // 配置是否可清空选择
  78. clearable: {
  79. type: Boolean,
  80. default() {
  81. return false
  82. },
  83. },
  84. // 配置多选时是否将选中值按文字的形式展示
  85. collapseTags: {
  86. type: Boolean,
  87. default() {
  88. return false
  89. },
  90. },
  91. nodeKey: {
  92. type: String,
  93. default() {
  94. return 'id'
  95. },
  96. },
  97. // 显示复选框情况下,是否严格遵循父子不互相关联
  98. checkStrictly: {
  99. type: Boolean,
  100. default() {
  101. return false
  102. },
  103. },
  104. // 默认选中的节点key数组
  105. checkedKeys: {
  106. type: Array,
  107. default() {
  108. return []
  109. },
  110. },
  111. size: {
  112. type: String,
  113. default() {
  114. return ''
  115. },
  116. },
  117. width: {
  118. type: Number,
  119. default() {
  120. return 500
  121. },
  122. },
  123. height: {
  124. type: Number,
  125. default() {
  126. return 300
  127. },
  128. },
  129. },
  130. data() {
  131. return {
  132. isShowSelect: false, // 是否显示树状选择器
  133. options: [],
  134. selectedData: [], // 选中的节点
  135. style:
  136. 'width:' + (this.width - 24) + 'px;' + 'height:' + this.height + 'px;',
  137. selectStyle: 'width:' + (this.width + 24) + 'px;',
  138. checkedIds: [],
  139. checkedData: [],
  140. }
  141. },
  142. watch: {
  143. isShowSelect(val) {
  144. // 隐藏select自带的下拉框
  145. this.$refs.select.blur()
  146. },
  147. checkedKeys: {
  148. handler(val) {
  149. // console.log('checkedKeys', val)
  150. if (!val) return
  151. this.checkedKeys = val
  152. this.initCheckedData()
  153. },
  154. deep: true,
  155. },
  156. },
  157. mounted() {
  158. this.initCheckedData()
  159. },
  160. methods: {
  161. // 单选时点击tree节点,设置select选项
  162. setSelectOption(node) {
  163. const tmpMap = {}
  164. tmpMap.value = node.key
  165. tmpMap.label = node.label
  166. this.options = []
  167. this.options.push(tmpMap)
  168. this.selectedData = node.key
  169. },
  170. // 单选,选中传进来的节点
  171. checkSelectedNode(checkedKeys) {
  172. // console.log("checkedKeys",checkedKeys);
  173. const item = checkedKeys[0]
  174. this.$refs.tree.setCurrentKey(item)
  175. const node = this.$refs.tree.getNode(item)
  176. this.setSelectOption(node)
  177. },
  178. // 多选,勾选上传进来的节点
  179. checkSelectedNodes(checkedKeys) {
  180. // console.log("checkedKeys", checkedKeys);
  181. // 优化select回显显示 有个延迟的效果
  182. const that = this
  183. setTimeout(function () {
  184. that.$refs.tree.setCheckedKeys(checkedKeys)
  185. }, 10)
  186. },
  187. // 单选,清空选中
  188. clearSelectedNode() {
  189. this.selectedData = []
  190. this.$refs.tree.setCurrentKey(null)
  191. },
  192. // 多选,清空所有勾选
  193. clearSelectedNodes() {
  194. const checkedKeys = this.$refs.tree.getCheckedKeys() // 所有被选中的节点的 key 所组成的数组数据
  195. for (let i = 0; i < checkedKeys.length; i++) {
  196. this.$refs.tree.setChecked(checkedKeys[i], false)
  197. }
  198. },
  199. initCheckedData() {
  200. if (this.multiple) {
  201. // 多选
  202. if (this.checkedKeys.length > 0) {
  203. this.checkSelectedNodes(this.checkedKeys)
  204. } else {
  205. this.clearSelectedNodes()
  206. }
  207. } else {
  208. // 单选
  209. if (this.checkedKeys.length > 0) {
  210. this.checkSelectedNode(this.checkedKeys)
  211. } else {
  212. this.clearSelectedNode()
  213. }
  214. }
  215. },
  216. popoverHide() {
  217. if (this.multiple) {
  218. this.checkedIds = this.$refs.tree.getCheckedKeys() // 所有被选中的节点的 key 所组成的数组数据
  219. this.checkedData = this.$refs.tree.getCheckedNodes() // 所有被选中的节点所组成的数组数据
  220. } else {
  221. this.checkedIds = this.$refs.tree.getCurrentKey()
  222. this.checkedData = this.$refs.tree.getCurrentNode()
  223. }
  224. this.$emit('popoverHide', this.checkedIds, this.checkedData)
  225. },
  226. // 单选,节点被点击时的回调,返回被点击的节点数据
  227. handleNodeClick(data, node) {
  228. if (!this.multiple) {
  229. this.setSelectOption(node)
  230. this.isShowSelect = !this.isShowSelect
  231. this.$emit('change', this.selectedData)
  232. }
  233. },
  234. // 多选,节点勾选状态发生变化时的回调
  235. handleCheckChange() {
  236. const checkedKeys = this.$refs.tree.getCheckedKeys() // 所有被选中的节点的 key 所组成的数组数据
  237. this.options = checkedKeys.map((item) => {
  238. const node = this.$refs.tree.getNode(item) // 所有被选中的节点对应的node
  239. const tmpMap = {}
  240. tmpMap.value = node.key
  241. tmpMap.label = node.label
  242. return tmpMap
  243. })
  244. this.selectedData = this.options.map((item) => {
  245. return item.value
  246. })
  247. this.$emit('change', this.selectedData)
  248. },
  249. // 多选,删除任一select选项的回调
  250. removeSelectedNodes(val) {
  251. this.$refs.tree.setChecked(val, false)
  252. const node = this.$refs.tree.getNode(val)
  253. if (!this.checkStrictly && node.childNodes.length > 0) {
  254. this.treeToList(node).map((item) => {
  255. if (item.childNodes.length <= 0) {
  256. this.$refs.tree.setChecked(item, false)
  257. }
  258. })
  259. this.handleCheckChange()
  260. }
  261. this.$emit('change', this.selectedData)
  262. },
  263. treeToList(tree) {
  264. let queen = []
  265. const out = []
  266. queen = queen.concat(tree)
  267. while (queen.length) {
  268. const first = queen.shift()
  269. if (first.childNodes) {
  270. queen = queen.concat(first.childNodes)
  271. }
  272. out.push(first)
  273. }
  274. return out
  275. },
  276. // 单选,清空select输入框的回调
  277. removeSelectedNode() {
  278. this.clearSelectedNode()
  279. this.$emit('change', this.selectedData)
  280. },
  281. // 选中的select选项改变的回调
  282. changeSelectedNodes(selectedData) {
  283. // 多选,清空select输入框时,清除树勾选
  284. if (this.multiple && selectedData.length <= 0) {
  285. this.clearSelectedNodes()
  286. }
  287. this.$emit('change', this.selectedData)
  288. },
  289. },
  290. }
  291. </script>
  292. <style scoped>
  293. .mask {
  294. width: 100%;
  295. height: 100%;
  296. position: fixed;
  297. top: 0;
  298. left: 0;
  299. opacity: 0;
  300. z-index: 11;
  301. }
  302. .common-tree {
  303. overflow: auto;
  304. }
  305. .tree-select {
  306. z-index: 111;
  307. }
  308. </style>