treeSelect.vue 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. <template>
  2. <el-select v-model="valueTitle" :size="size" :disabled="disabled" :clearable="clearable" :placeholder="placeholderText" @clear="clearHandle">
  3. <el-option :value="valueTitle" :label="valueTitle" class="options">
  4. <el-tree id="tree-option"
  5. ref="selectTree"
  6. :accordion="accordion"
  7. :data="optionData"
  8. :show-checkbox="showCheckbox"
  9. :props="props"
  10. highlight-current
  11. :check-strictly="checkStrictly"
  12. :check-on-click-node="checkOnClickNode"
  13. :node-key="props.value"
  14. :default-expanded-keys="defaultExpandedKey"
  15. @check-change="handleCheckChange"
  16. @node-click="handleNodeClick">
  17. </el-tree>
  18. </el-option>
  19. </el-select>
  20. </template>
  21. <script>
  22. export default {
  23. name: 'el-tree-select',
  24. props: {
  25. /* 配置项 */
  26. props: {
  27. type: Object,
  28. default: () => {
  29. return {
  30. value: 'id', // ID字段名
  31. label: 'label', // 显示名称
  32. children: 'children' // 子级字段名
  33. }
  34. }
  35. },
  36. /* 选项列表数据(树形结构的对象数组) */
  37. data: {
  38. type: Array,
  39. default: () => { return [] }
  40. },
  41. /* 选项列表数据(树形结构的对象数组) */
  42. list: {
  43. type: Array,
  44. default: () => { return null }
  45. },
  46. /* 初始值 */
  47. value: {
  48. type: String,
  49. default: () => { return '' }
  50. },
  51. /* 初始值 */
  52. url: {
  53. type: String,
  54. default: () => { return null }
  55. },
  56. disabled: {
  57. type: Boolean,
  58. dafault: () => { return false }
  59. },
  60. showCheckbox: {
  61. type: Boolean,
  62. dafault: () => { return false }
  63. },
  64. /* 初始值 */
  65. label: {
  66. type: String,
  67. default: () => { return '' }
  68. },
  69. /* 可清空选项 */
  70. clearable: {
  71. type: Boolean,
  72. default: () => { return true }
  73. },
  74. /* 自动收起 */
  75. accordion: {
  76. type: Boolean,
  77. default: () => { return false }
  78. },
  79. size: {
  80. type: String,
  81. default: () => { return 'small' }
  82. },
  83. placeholder: {
  84. type: String,
  85. default: () => { return '请选择' }
  86. },
  87. isOnlySelectLeaf: {
  88. type: Boolean,
  89. default: () => {
  90. return false
  91. }
  92. },
  93. // 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false
  94. checkStrictly: {
  95. type: Boolean,
  96. default: () => {
  97. return false
  98. }
  99. },
  100. // 是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点。
  101. checkOnClickNode: {
  102. type: Boolean,
  103. default: () => {
  104. return false
  105. }
  106. }
  107. },
  108. data () {
  109. return {
  110. valueId: this.value, // 初始值
  111. valueTitle: this.label,
  112. defaultExpandedKey: [],
  113. placeholderText: this.placeholder,
  114. treeList: [],
  115. valueData: this.data
  116. }
  117. },
  118. created () {
  119. if (this.url !== null) {
  120. this.placeholderText = '加载数据中...'
  121. let interval = setInterval(() => {
  122. this.placeholderText = this.placeholderText + '.'
  123. }, 500)
  124. this.$http({
  125. url: this.url,
  126. method: 'get'
  127. }).then((data) => {
  128. this.valueData = data
  129. this.setTreeList(this.valueData)
  130. this.$nextTick(() => {
  131. this.initHandle()
  132. this.placeholderText = this.placeholder
  133. clearInterval(interval)
  134. })
  135. })
  136. } else {
  137. this.valueData = this.data
  138. this.setTreeList(this.valueData)
  139. }
  140. },
  141. methods: {
  142. setTreeList (datas) { // 遍历树 获取id数组
  143. for (var i in datas) {
  144. this.treeList.push(datas[i])
  145. if (datas[i].children) {
  146. this.setTreeList(datas[i].children)
  147. }
  148. }
  149. },
  150. // 初始化值
  151. initHandle () {
  152. if (this.valueId) {
  153. if (this.showCheckbox) {
  154. let ids = this.valueId.split(',')
  155. this.$refs.selectTree.setCheckedKeys(ids)
  156. let titles = []
  157. ids.forEach((id) => {
  158. this.treeList.forEach((d) => {
  159. if (id === d[this.props.value]) {
  160. titles.push(d[this.props.label])
  161. }
  162. })
  163. })
  164. this.valueTitle = titles.join(',')
  165. } else if (this.$refs.selectTree.getNode(this.valueId)) {
  166. this.valueTitle = this.$refs.selectTree.getNode(this.valueId).data[this.props.label] // 初始化显示
  167. this.$refs.selectTree.setCurrentKey(this.valueId) // 设置默认选中
  168. this.defaultExpandedKey = [this.valueId] // 设置默认展开
  169. }
  170. }
  171. this.initScroll()
  172. },
  173. getNode (id) {
  174. return this.$refs.selectTree.getNode(id)
  175. },
  176. // 初始化滚动条
  177. initScroll () {
  178. this.$nextTick(() => {
  179. let scrollWrap = document.querySelectorAll('.el-scrollbar .el-select-dropdown__wrap')[0]
  180. let scrollBar = document.querySelectorAll('.el-scrollbar .el-scrollbar__bar')
  181. if (scrollWrap) { scrollWrap.style.cssText = 'margin: 0px; max-height: none; overflow: hidden;' }
  182. if (scrollBar) {
  183. scrollBar.forEach(ele => {
  184. // eslint-disable-next-line no-return-assign
  185. return ele.style.width = 0
  186. })
  187. }
  188. })
  189. },
  190. // 切换选项
  191. handleNodeClick (node) {
  192. if (this.showCheckbox) {
  193. return
  194. }
  195. if (node['disabled']) {
  196. // this.$message.warning('节点(' + node[this.props.label] + ')被禁止选择,请重新选择。')
  197. return
  198. }
  199. if (this.isOnlySelectLeaf && node.children.length > 0) {
  200. this.$message.warning('不能选择根节点(' + node[this.props.label] + ')请重新选择。')
  201. return
  202. }
  203. this.valueTitle = node[this.props.label]
  204. this.valueId = node[this.props.value]
  205. this.$emit('getValue', this.valueId, this.valueTitle, node)
  206. },
  207. handleCheckChange (data, checked, indeterminate) {
  208. let nodes = this.$refs.selectTree.getCheckedNodes()
  209. this.valueTitle = nodes.map((node) => {
  210. return node[this.props.label]
  211. }).join(',')
  212. this.valueId = nodes.map((node) => {
  213. return node[this.props.value]
  214. }).join(',')
  215. this.$emit('getValue', this.valueId, this.valueTitle)
  216. },
  217. // 清除选中
  218. clearHandle () {
  219. this.valueTitle = ''
  220. this.valueId = null
  221. this.defaultExpandedKey = []
  222. this.clearSelected()
  223. this.$emit('getValue', null, null, null)
  224. },
  225. /* 清空选中样式 */
  226. clearSelected () {
  227. let allNode = document.querySelectorAll('#tree-option .el-tree-node')
  228. allNode.forEach((element) => element.classList.remove('is-current'))
  229. }
  230. },
  231. watch: {
  232. value () {
  233. this.valueId = this.value
  234. if (this.value === '' || this.value === null || this.value === undefined) {
  235. this.clearHandle()
  236. } else {
  237. this.initHandle()
  238. }
  239. },
  240. data () {
  241. this.valueData = this.data
  242. }
  243. },
  244. computed: {
  245. optionData () {
  246. if (this.list) {
  247. let cloneData = JSON.parse(JSON.stringify(this.list)) // 对源数据深度克隆
  248. return cloneData.filter(father => { // 循环所有项,并添加children属性
  249. let branchArr = cloneData.filter(child => father.id === child.parentId) // 返回每一项的子级数组
  250. // eslint-disable-next-line no-unused-expressions
  251. branchArr.length > 0 ? father.children = branchArr : '' // 给父级添加一个children属性,并赋值
  252. return father.parentId === '0' // 返回第一层
  253. })
  254. } else {
  255. return this.valueData
  256. }
  257. }
  258. }
  259. }
  260. </script>
  261. <!-- Add "scoped" attribute to limit CSS to this component only -->
  262. <style scoped>
  263. .el-select{
  264. width: 100%;
  265. }
  266. .el-scrollbar .el-scrollbar__view .el-select-dropdown__item{
  267. height: auto;
  268. max-height: 274px;
  269. padding: 0;
  270. overflow: hidden;
  271. overflow-y: auto;
  272. }
  273. .el-select-dropdown__item.selected{
  274. font-weight: normal;
  275. }
  276. ul li >>>.el-tree .el-tree-node__content{
  277. height:auto;
  278. padding: 0 20px;
  279. }
  280. .el-tree-node__label{
  281. font-weight: normal;
  282. }
  283. .el-tree >>>.is-current .el-tree-node__label{
  284. color: #409EFF;
  285. font-weight: 700;
  286. }
  287. .el-tree >>>.is-current .el-tree-node__children .el-tree-node__label{
  288. color:#606266;
  289. font-weight: normal;
  290. }
  291. /* 开发禁用 */
  292. /* .el-tree-node:focus>.el-tree-node__content{
  293. background-color:transparent;
  294. background-color: #f5f7fa;
  295. color: #c0c4cc;
  296. cursor: not-allowed;
  297. }
  298. .el-tree-node__content:hover{
  299. background-color: #f5f7fa;
  300. } */
  301. </style>