wk-base-filter.vue 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <template>
  2. <view class="wk-base-filter">
  3. <view class="menu">
  4. <template v-for="(tab, index) in copyTabs">
  5. <view
  6. :key="index"
  7. class="menu-item"
  8. @click="handleOpenFilter(index)">
  9. <text class="text">
  10. {{ tab.tabLabel }}
  11. </text>
  12. <text class="icon" />
  13. <!--:class="{ active: tab.type === 'filter' && !$isEmpty(tab.value) }" -->
  14. </view>
  15. <view v-if="index < copyTabs.length - 1" :key="index | filterKey" class="line" />
  16. </template>
  17. </view>
  18. <uni-popup
  19. ref="filterPopup"
  20. radius="20rpx 20rpx 0 0"
  21. type="bottom">
  22. <view class="popup">
  23. <view class="popup-header">
  24. <text class="wk wk-close icon" @click="handleCloseFilter" />
  25. <view class="wk-tabs">
  26. <view
  27. v-for="(tab, index) in copyTabs"
  28. :key="index"
  29. :class="{active: tabIndex === index }"
  30. class="wk-tab-item"
  31. @click="handleToggleTabs(index)">
  32. {{ tab.label }}
  33. </view>
  34. </view>
  35. </view>
  36. <scroll-view
  37. :style="{maxHeight: maxHeight + 'px'}"
  38. scroll-y
  39. class="popup-content">
  40. <view class="options-group">
  41. <view
  42. v-for="(item, index) in popupOptions"
  43. :key="index"
  44. :class="{active: index === activeOptionIndex}"
  45. class="options-group-item"
  46. @click="handleOptionClick(index, item)">
  47. <text class="text">
  48. {{ item.label }}
  49. </text>
  50. <text v-if="item.showMoreIcon" class="wk wk-arrow-right icon" />
  51. <text v-else-if="index === activeOptionIndex" class="wk wk-check icon" />
  52. </view>
  53. </view>
  54. </scroll-view>
  55. </view>
  56. </uni-popup>
  57. </view>
  58. </template>
  59. <script>
  60. import { deepCopy } from '@/utils/lib.js'
  61. export default {
  62. name: 'WkBaseFilter',
  63. filters: {
  64. filterKey(key) {
  65. return 'empty_' + key
  66. }
  67. },
  68. props: {
  69. tabs: {
  70. type: Array,
  71. required: true
  72. }
  73. },
  74. data() {
  75. return {
  76. maxHeight: 0,
  77. tabIndex: null,
  78. popupOptions: [],
  79. activeOptionIndex: null,
  80. copyTabs: []
  81. }
  82. },
  83. watch: {
  84. tabs: {
  85. handler(val) {
  86. this.initCom()
  87. },
  88. deep: true
  89. }
  90. },
  91. mounted() {
  92. this.initCom()
  93. },
  94. methods: {
  95. initCom() {
  96. const sysInfo = uni.getSystemInfoSync()
  97. this.maxHeight = sysInfo.windowHeight * 0.75
  98. // 编译成微信小程序时直接修改 tabs 会导致无法取 tabLabel 值
  99. this.copyTabs = deepCopy(this.tabs).map(tab => {
  100. let tabLabel = tab.label
  101. if (!this.$isEmpty(tab.value)) {
  102. const findRes = tab.options.find(o => o.value == tab.value)
  103. if (findRes) {
  104. tabLabel = findRes.label
  105. }
  106. }
  107. tab.tabLabel = tabLabel
  108. return tab
  109. // this.$set(tab, 'tabLabel', tabLabel)
  110. })
  111. // console.log('tabs', this.tabs, this.copyTabs)
  112. },
  113. handleOpenFilter(index) {
  114. this.handleToggleTabs(index)
  115. this.$refs.filterPopup.open()
  116. },
  117. handleCloseFilter() {
  118. this.$refs.filterPopup.close()
  119. this.tabIndex = null
  120. this.clearOptionsData()
  121. },
  122. handleToggleTabs(index) {
  123. const tab = this.copyTabs[index]
  124. this.clearOptionsData()
  125. this.tabIndex = index
  126. this.popupOptions = tab.options
  127. this.activeOptionIndex = tab.valueData
  128. },
  129. handleOptionClick(index, option) {
  130. this.activeOptionIndex = index
  131. const tabItem = this.copyTabs[this.tabIndex]
  132. tabItem.tabLabel = option.label
  133. tabItem.value = option.value
  134. tabItem.valueData = index
  135. this.$set(this.copyTabs, this.tabIndex, tabItem)
  136. if (option.showMoreIcon) {
  137. const that = this
  138. const callback = () => that.handleCloseFilter()
  139. this.$emit('filter', tabItem, this.tabIndex, this.copyTabs, callback)
  140. } else {
  141. this.$emit('filter', tabItem, this.tabIndex, this.copyTabs)
  142. this.handleCloseFilter()
  143. }
  144. },
  145. setDefault(data) {
  146. this.copyTabs.forEach(tab => {
  147. if (
  148. data.hasOwnProperty(tab.field) &&
  149. !this.$isEmpty(data[tab.field])
  150. ) {
  151. const findIndex = tab.options.findIndex(o => o.value === data[tab.field])
  152. if (findIndex !== -1) {
  153. this.$set(tab, 'valueData', findIndex)
  154. this.$set(tab, 'value', data[tab.field])
  155. this.$set(tab, 'tabLabel', tab.options[findIndex].label)
  156. }
  157. }
  158. })
  159. },
  160. clearOptionsData() {
  161. this.popupOptions = null
  162. this.activeOptionIndex = null
  163. this.isBusinessStauts = false
  164. }
  165. }
  166. }
  167. </script>
  168. <style scoped lang="scss">
  169. .wk-tabs {
  170. .wk-tab-item {
  171. padding: 15rpx 0;
  172. // &::after {
  173. // width: 50%;
  174. // }
  175. &.active {
  176. color: $theme-color;
  177. }
  178. }
  179. }
  180. .wk-base-filter {
  181. .menu {
  182. width: 100%;
  183. height: 80rpx;
  184. @include center;
  185. .menu-item {
  186. flex: 1;
  187. font-size: $wk-font-base;
  188. color: $gray;
  189. padding: 0 10rpx;
  190. overflow: hidden;
  191. @include center;
  192. .text {
  193. max-width: calc(100% - 40rpx);
  194. @include ellipsis;
  195. }
  196. .icon {
  197. width: 0;
  198. height: 0;
  199. border-left: 10rpx solid transparent;
  200. border-right: 10rpx solid transparent;
  201. border-top: 10rpx solid $gray;
  202. margin-left: 10rpx;
  203. &.active {
  204. border-top: 10rpx solid $theme-color;
  205. }
  206. }
  207. }
  208. .line {
  209. width: 1rpx;
  210. height: 40rpx;
  211. margin: 0 auto;
  212. background-color: #CBCBCB;
  213. }
  214. }
  215. .popup {
  216. padding: 10rpx 36rpx 20rpx 36rpx;
  217. .popup-header {
  218. position: relative;
  219. @include center;
  220. .text {
  221. font-size: $wk-font-large;
  222. }
  223. .icon {
  224. position: absolute;
  225. left: -20rpx;
  226. top: 50%;
  227. transform: translateY(-50%);
  228. color: $gray;
  229. font-size: $wk-font-medium;
  230. padding: 20rpx;
  231. }
  232. }
  233. .popup-content {
  234. width: 100%;
  235. min-height: 700rpx;
  236. margin-top: 20rpx;
  237. overflow: hidden;
  238. display: flex;
  239. align-items: flex-start;
  240. justify-content: flex-start;
  241. flex-direction: column;
  242. .options-group {
  243. width: 100%;
  244. .options-group-item {
  245. color: $dark;
  246. font-size: $wk-font-base;
  247. border-bottom: 1rpx solid $border-color;
  248. padding: 22rpx 0;
  249. @include left;
  250. .text {
  251. flex: 1;
  252. }
  253. .icon {
  254. font-size: $wk-font-medium;
  255. color: inherit;
  256. line-height: 1;
  257. }
  258. &.active {
  259. color: $theme-color;
  260. }
  261. }
  262. }
  263. }
  264. }
  265. }
  266. </style>