e75e2945f156e9d6e17a0e9a94ee313c8370a955.svn-base 9.8 KB

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