index.vue 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. <template>
  2. <div
  3. v-loading="loading"
  4. class="l-flex--col u-color--black u-font-size--sm u-relative"
  5. :class="{ shrink: shrinkState }"
  6. >
  7. <el-tabs
  8. v-if="exact"
  9. v-model="active"
  10. class="c-tabs has-bottom-padding"
  11. >
  12. <el-tab-pane
  13. label="按部门"
  14. name="group"
  15. />
  16. <el-tab-pane
  17. label="按分辨率"
  18. name="ratio"
  19. />
  20. <el-tab-pane
  21. label="按宽高比"
  22. name="productType"
  23. />
  24. </el-tabs>
  25. <warning
  26. v-if="error"
  27. @click="getDevices"
  28. />
  29. <template v-else>
  30. <div class="l-flex__none l-flex--row c-sibling-item--v u-overflow--hidden">
  31. <div class="l-flex__auto" />
  32. <search-input
  33. v-model.trim="deviceName"
  34. class="l-flex__none"
  35. placeholder="设备名称"
  36. @search="onSearch"
  37. />
  38. </div>
  39. <c-tree
  40. class="l-flex__fill c-sibling-item--v"
  41. :root-option="rootOption"
  42. />
  43. </template>
  44. <i
  45. v-if="shrink"
  46. class="o-shrink-icon u-color--black u-font-size--lg has-active"
  47. :class="iconClass"
  48. @click="onToggleShrink"
  49. />
  50. </div>
  51. </template>
  52. <script>
  53. import {
  54. getDepartmentDeviceTree,
  55. getProduct
  56. } from '@/api/device'
  57. import treeMixin from '../tree.js'
  58. export default {
  59. name: 'DeviceTree',
  60. mixins: [treeMixin],
  61. props: {
  62. shrink: {
  63. type: [Boolean, String],
  64. default: false
  65. },
  66. exact: {
  67. type: [Boolean, String],
  68. default: false
  69. },
  70. filter: {
  71. type: Function,
  72. default: null
  73. }
  74. },
  75. data () {
  76. return {
  77. shrinkState: false,
  78. loading: false,
  79. error: false,
  80. active: 'group',
  81. deviceName: ''
  82. }
  83. },
  84. computed: {
  85. isGroupTab () {
  86. return this.active === 'group'
  87. },
  88. iconClass () {
  89. return this.shrinkState ? 'el-icon-d-arrow-right' : 'el-icon-d-arrow-left'
  90. }
  91. },
  92. watch: {
  93. active: {
  94. handler () {
  95. this.getDevices()
  96. },
  97. immediate: true
  98. }
  99. },
  100. methods: {
  101. onToggleShrink () {
  102. this.shrinkState = !this.shrinkState
  103. },
  104. getDevices () {
  105. this.loading = true
  106. this.error = false
  107. this.getDevicesByActive().then(
  108. data => {
  109. this.$nodes = data
  110. this.onSearch()
  111. },
  112. () => {
  113. this.error = true
  114. }
  115. ).finally(() => {
  116. this.loading = false
  117. })
  118. },
  119. getDevicesByActive () {
  120. if (this.$productCache) {
  121. return this.initTabCache()
  122. }
  123. const cache = this[`$${this.active}Cache`]
  124. if (cache) {
  125. return Promise.resolve(cache)
  126. }
  127. return getDepartmentDeviceTree().then(({ data }) => {
  128. const productCacheOptions = {
  129. key: 'productId',
  130. ids: {},
  131. map: {},
  132. cache: []
  133. }
  134. this.$groupCache = this.iterateNode(data, productCacheOptions).children
  135. if (this.exact) {
  136. this.$productCache = productCacheOptions.cache
  137. }
  138. return this.getDevicesByActive()
  139. })
  140. },
  141. async initTabCache () {
  142. for (let i = 0; i < this.$productCache.length; i++) {
  143. if (!this.$productCache[i].resolutionRatio) {
  144. const { data: { resolutionRatio, productTypeName } } = await getProduct(this.$productCache[i].id)
  145. this.$productCache[i].resolutionRatio = resolutionRatio
  146. this.$productCache[i].productTypeName = productTypeName
  147. this.$productCache[i].children.forEach(device => {
  148. device.resolutionRatio = resolutionRatio
  149. })
  150. }
  151. }
  152. const ratioMap = {}
  153. const productTypeMap = {}
  154. this.$productCache.forEach(({ resolutionRatio, productTypeName, children }) => {
  155. if (!ratioMap[resolutionRatio]) {
  156. ratioMap[resolutionRatio] = []
  157. }
  158. ratioMap[resolutionRatio] = [...ratioMap[resolutionRatio], ...children]
  159. if (!productTypeMap[productTypeName]) {
  160. productTypeMap[productTypeName] = []
  161. }
  162. productTypeMap[productTypeName] = [...productTypeMap[productTypeName], ...children]
  163. })
  164. this.$ratioCache = Object.keys(ratioMap).map(key => {
  165. return {
  166. id: key,
  167. name: key,
  168. children: ratioMap[key]
  169. }
  170. })
  171. this.$productTypeCache = Object.keys(productTypeMap).map(key => {
  172. return {
  173. id: key,
  174. name: key,
  175. children: productTypeMap[key]
  176. }
  177. })
  178. this.$productCache = null
  179. return this.getDevicesByActive()
  180. },
  181. iterateNode ({ id = 'root', name = '我的部门', children, devices }, productCacheOptions) {
  182. const deviceArr = []
  183. devices.forEach(device => {
  184. const item = this.transform(device)
  185. this.setCache(item, productCacheOptions)
  186. deviceArr.push(item)
  187. })
  188. return {
  189. id: `dir_${id}`,
  190. name,
  191. children: [
  192. ...children.map(child => this.iterateNode(child, productCacheOptions)),
  193. ...deviceArr
  194. ]
  195. }
  196. },
  197. transform ({ id, name, productId }) {
  198. return {
  199. id, name, productId
  200. }
  201. },
  202. setCache (device, { key, ids, map, cache }) {
  203. const id = device.id
  204. if (!ids[id]) {
  205. const value = device[key]
  206. if (!map[value]) {
  207. map[value] = []
  208. cache.push({
  209. id: value,
  210. children: map[value]
  211. })
  212. }
  213. ids[id] = 1
  214. map[value].push(device)
  215. }
  216. },
  217. onSearch () {
  218. const regx = this.deviceName ? new RegExp(this.deviceName) : null
  219. const rootOption = this.createNode()
  220. this.setNodes(rootOption, this.onFilter(rootOption, this.$nodes, regx))
  221. this.rootOption = rootOption
  222. this.customExpand = !!this.deviceName
  223. },
  224. onFilter (parent, nodes, regx) {
  225. if (regx) {
  226. nodes = nodes.filter(({ children, name }) => {
  227. if (children) {
  228. return true
  229. }
  230. return regx.test(name)
  231. })
  232. }
  233. const childNodes = nodes.map(({ children, ...node }) => {
  234. if (!children) {
  235. const leafNode = this.createLeafNode(parent, node)
  236. if (this.filter) {
  237. leafNode.disabled = !this.filter(node)
  238. }
  239. return leafNode
  240. }
  241. const subNode = this.createNode(node, parent)
  242. this.setNodes(subNode, this.onFilter(subNode, children, regx))
  243. return subNode
  244. })
  245. if (this.hideDisabled) {
  246. return childNodes.filter(({ disabled }) => !disabled)
  247. }
  248. return childNodes
  249. },
  250. reset () {
  251. this.onSearch()
  252. }
  253. }
  254. }
  255. </script>
  256. <style lang="scss" scoped>
  257. .shrink {
  258. box-sizing: border-box;
  259. width: $spacing !important;
  260. }
  261. .o-shrink-icon {
  262. position: absolute;
  263. top: 50%;
  264. right: 0;
  265. transform: translateY(-50%);
  266. }
  267. </style>