index.vue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. <template>
  2. <el-select
  3. v-loading.fullscreen="companyChangeLoading"
  4. :element-loading-text="'正在切换至 ' + getUserCompany().name + ' ......'"
  5. element-loading-background="rgba(0, 0, 0, 0.8)"
  6. element-loading-spinner="el-icon-loading"
  7. :value="value ? value : currentCompany"
  8. :remote-method="handleRemoteSearch"
  9. :placeholder="placeholder"
  10. style="width:380px"
  11. :disabled="disabled"
  12. reserve-keyword
  13. :size="size"
  14. filterable
  15. remote
  16. @change="setCurrentCompany"
  17. >
  18. <!-- 当selectAll为true 且为超管账号允许选择所有公司 -->
  19. <el-option v-if="selectAll && isSupertube" label="所有公司" value />
  20. <el-option
  21. v-for="(company, index) in companylist"
  22. :key="index"
  23. class="custom-company"
  24. :label="company.code + ' / ' + company.name"
  25. :value="company.code"
  26. />
  27. <p v-if="state.loading" class="company-loading">
  28. <i class="el-icon-loading" />
  29. 加载中...
  30. </p>
  31. <p v-if="state.noMore" class="company-more">没有更多数据了...</p>
  32. </el-select>
  33. </template>
  34. <script>
  35. import { mapState } from 'vuex'
  36. import { convertCompanylist } from '@/utils'
  37. import { requsetSupertubeCompany, requsetUserBindingCompany } from '@/apis/user'
  38. import { isBusinessCompany, isOnlyBusinessCompanyPath } from './utils'
  39. import { getUserCompany } from '@/utils/auth'
  40. export default {
  41. /**
  42. * @param placeholder
  43. * @param size 选择器尺寸
  44. * @param disabled 是否禁用
  45. * @param selectAll 超管账号是否可以选择所有公司
  46. * @param global 是否设置全局选中公司(全局使用/组件独立使用)
  47. */
  48. props: {
  49. selectAll: {
  50. type: Boolean,
  51. default: false
  52. },
  53. size: {
  54. type: String,
  55. default: 'small'
  56. },
  57. placeholder: {
  58. type: String,
  59. default: ''
  60. },
  61. disabled: {
  62. type: Boolean,
  63. default: false
  64. },
  65. global: {
  66. type: Boolean,
  67. default: false
  68. },
  69. value: {
  70. type: String,
  71. default: ''
  72. }
  73. },
  74. computed: {
  75. ...mapState({
  76. currentCompany: state => state.user.currentCompany,
  77. companylist: state => state.user.companylist,
  78. isSupertube: state => state.user.isSupertube
  79. })
  80. },
  81. data() {
  82. return {
  83. ScrollWrapper: null,
  84. initialization: true,
  85. companyChangeLoading: false,
  86. state: {
  87. loading: false,
  88. noMore: false
  89. },
  90. params: {
  91. name: '',
  92. page: 2,
  93. size: 10
  94. }
  95. }
  96. },
  97. async mounted() {
  98. this.initalData()
  99. this.ScrollWrapper = await this.getScrollWrapper()
  100. this.ScrollWrapper && this.bindEvent(this.ScrollWrapper)
  101. },
  102. beforeDetoryed() {
  103. this.ScrollWrapper && this.removeEvent(this.ScrollWrapper)
  104. },
  105. methods: {
  106. getUserCompany,
  107. async initalData() {
  108. // 区分是否超管用户请求不同接口
  109. this.state.loading = true
  110. const api = this.isSupertube ? requsetSupertubeCompany : requsetUserBindingCompany
  111. const { page, size, name } = this.params
  112. const params = {
  113. [this.isSupertube ? 'name' : 'companyName']: name,
  114. page,
  115. size
  116. }
  117. const { code, data } = await api(params)
  118. if (code === 0) {
  119. if (data.list.length < 10) this.state.noMore = true
  120. this.state.loading = false
  121. this.params.page++
  122. const _list = this.isSupertube ? data.list : convertCompanylist(data.list)
  123. this.$store.commit('user/setCompanylist', [...this.companylist, ..._list])
  124. return this.isSupertube ? data.list : convertCompanylist(data.list)
  125. }
  126. },
  127. setCurrentCompany(currentCompany) {
  128. this.$emit('update:value', currentCompany)
  129. this.$emit('change', currentCompany)
  130. if (!this.global) return null // 作为通用组件使用不设置全局选中公司
  131. // 只能选择业务公司的页面
  132. const path = this.$route.path
  133. if (isOnlyBusinessCompanyPath(path) && !isBusinessCompany(currentCompany)) {
  134. this.$message.warning('该页面只能选择业务公司')
  135. return
  136. }
  137. this.$store.commit('user/setCurrentCompany', currentCompany)
  138. if (this.isSupertube) return null // 超管用户不刷新路由
  139. this.changeRouterWithCompany()
  140. },
  141. async changeRouterWithCompany() {
  142. const { path, query } = this.$route
  143. query.redirect = path
  144. const keys = Object.keys(query).filter(key => key && key !== 'error')
  145. // 1.保存透传参数
  146. let queryString = ''
  147. const chunk = (qs, key, index) => {
  148. const next = `${key}=${query[key]}${index === keys.length ? '' : '&'}`
  149. return qs + next
  150. }
  151. // 2.重新获取用户信息和菜单
  152. this.companyChangeLoading = true
  153. const result = await this.$store.dispatch('user/reloadMenulist')
  154. this.companyChangeLoading = false
  155. switch (result) {
  156. /**
  157. * 登出
  158. */
  159. case 'noToken':
  160. await this.$store.dispatch('user/logout')
  161. this.$router.push(`/login`)
  162. break
  163. /**
  164. * 角色异常或该公司禁用,清空菜单返回首页
  165. */
  166. case 'disabled':
  167. this.$store.dispatch('user/disabledCompany')
  168. break
  169. /**
  170. * 切换路由
  171. */
  172. default:
  173. queryString = keys.reduce(chunk, '?')
  174. this.$router.replace('/reload' + queryString)
  175. break
  176. }
  177. },
  178. getScrollWrapper() {
  179. return new Promise(resolve => {
  180. setTimeout(() => {
  181. let optionEl = document.querySelector('.custom-company')
  182. if (optionEl && (optionEl = optionEl.parentElement)) {
  183. resolve(optionEl.parentElement)
  184. return
  185. }
  186. resolve(null)
  187. }, 500)
  188. })
  189. },
  190. async handleScroll() {
  191. const ScrollWrapper = this.ScrollWrapper
  192. const height = ScrollWrapper.clientHeight
  193. const scrollTop = ScrollWrapper.scrollTop
  194. const scrollHeight = ScrollWrapper.scrollHeight
  195. const { loading, noMore } = this.state
  196. // 是否允许下拉加载
  197. const isAllowLoad = !loading && !noMore
  198. if (height + scrollTop >= scrollHeight && isAllowLoad) {
  199. const list = await this.loadMore()
  200. // 保存到全局
  201. this.$store.commit('user/setCompanylist', [...this.companylist, ...(this.isSupertube ? list : convertCompanylist(list))])
  202. }
  203. },
  204. async loadMore() {
  205. // 区分超管用户和普通用户
  206. const api = this.isSupertube ? requsetSupertubeCompany : requsetUserBindingCompany
  207. this.state.loading = true
  208. const { page, size, name } = this.params
  209. const nameProp = this.isSupertube ? 'name' : 'companyName'
  210. const params = {
  211. [nameProp]: name,
  212. page,
  213. size
  214. }
  215. const { data } = await api(params)
  216. this.state.loading = false
  217. const isTransboundary = this.companylist.length >= Number(data.count) // 是否越界
  218. if (isTransboundary) {
  219. this.state.noMore = true
  220. this.sate.loading = false
  221. return []
  222. }
  223. this.params.page++
  224. return data.list
  225. },
  226. async handleRemoteSearch(name) {
  227. // 清空全局业务公司列表
  228. this.$store.commit('user/setCompanylist', [])
  229. this.params.name = name
  230. this.params.page = 1
  231. this.state.noMore = false
  232. this.state.loading = false
  233. // 获取列表数据
  234. const result = await this.initalData()
  235. if (result) this.$store.commit('user/setCompanylist', result)
  236. },
  237. bindEvent(ScrollWrapper) {
  238. ScrollWrapper.addEventListener('scroll', this.handleScroll)
  239. },
  240. removeEvent(ScrollWrapper) {
  241. ScrollWrapper.removeEventListener('scroll', this.handleScroll)
  242. }
  243. }
  244. }
  245. </script>
  246. <style lang="scss" scoped>
  247. .company-more {
  248. text-align: center;
  249. color: #999;
  250. font-size: 14px;
  251. line-height: 38px;
  252. }
  253. .company-loading {
  254. display: flex;
  255. font-size: 14px;
  256. justify-content: center;
  257. align-items: center;
  258. color: #999;
  259. gap: 10px;
  260. line-height: 38px;
  261. }
  262. </style>