uv-drop-down-popup.vue 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. <template>
  2. <view class="uv-drop-down-popup">
  3. <uv-transition :show="show" mode="fade" :duration="300" :custom-style="overlayStyle" @click="clickOverlay">
  4. <view class="uv-dp__container" ref="uvDPContainer" :style="{height: `${height}px`}" @click.stop="blockClick">
  5. <view class="uv-dp__container__list" ref="uvDPList">
  6. <slot>
  7. <view class="uv-dp__container__list--item" v-for="(item,index) in list" :key="index" @click="clickHandler(item,index)" :style="[itemCustomStyle(index)]">
  8. <uv-text :text="item[keyName]" :size="getTextSize(index)" :color="getTextColor(index)"></uv-text>
  9. </view>
  10. </slot>
  11. </view>
  12. </view>
  13. </uv-transition>
  14. </view>
  15. </template>
  16. <script>
  17. import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js';
  18. import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js';
  19. import uvTransition from '@/uni_modules/uv-transition/components/uv-transition/uv-transition.vue'
  20. // #ifdef APP-NVUE
  21. const animation = uni.requireNativePlugin('animation');
  22. const dom = uni.requireNativePlugin('dom');
  23. // #endif
  24. /**
  25. * DropDownPopup 下拉框
  26. * @description 下拉筛选框
  27. * @tutorial https://ext.dcloud.net.cn/plugin?name=uv-drop-down
  28. * @property {String | Number} name 字段标识
  29. * @property {String | Number} zIndex 弹出层的层级
  30. * @property {String | Number} opacity 遮罩层的透明度
  31. * @property {Boolean} clickOverlayOnClose 是否允许点击遮罩层关闭弹窗
  32. * @property {Object} currentDropItem 当前下拉筛选菜单对象
  33. * @property {String} keyName 指定从当前下拉筛选菜单对象元素中读取哪个属性作为文本展示
  34. */
  35. export default {
  36. name: 'uv-drop-down-popup',
  37. components: {
  38. uvTransition // 注册导入的过渡组件
  39. },
  40. mixins: [mpMixin, mixin],
  41. props: {
  42. sign: {
  43. type: [String, Number],
  44. default: 'UVDROPDOWN'
  45. },
  46. zIndex: {
  47. type: [Number, String],
  48. default: 999
  49. },
  50. opacity: {
  51. type: [Number, String],
  52. default: 0.5
  53. },
  54. clickOverlayOnClose: {
  55. type: Boolean,
  56. default: true
  57. },
  58. // 当前下拉选项对象
  59. currentDropItem: {
  60. type: Object,
  61. default () {
  62. return {
  63. activeIndex: 0,
  64. child: []
  65. }
  66. }
  67. },
  68. keyName: {
  69. type: String,
  70. default: 'label'
  71. }
  72. },
  73. data() {
  74. return {
  75. show: false,
  76. rect: {},
  77. height: 0
  78. }
  79. },
  80. computed: {
  81. overlayStyle() {
  82. let { height = 0, top = 0 } = this.rect;
  83. // #ifdef H5
  84. top += this.$uv.sys().windowTop;
  85. // #endif
  86. const style = {
  87. position: 'fixed',
  88. top: `${top+height}px`,
  89. left: 0,
  90. right: 0,
  91. zIndex: this.zIndex,
  92. bottom: 0,
  93. 'background-color': `rgba(0, 0, 0, ${this.opacity})`
  94. }
  95. // 合并自定义样式
  96. // const mergedStyle = this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle));
  97. // 将样式对象转换为字符串
  98. // return Object.entries(mergedStyle).map(([key, value]) => `${key}:${value}`).join(';');
  99. // console.log(Object.entries(mergedStyle).map(([key, value]) => `${key}:${value}`).join(';'),999)
  100. // console.log(this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle)))
  101. return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
  102. },
  103. list() {
  104. try {
  105. return Array.isArray(this.currentDropItem.child) ? this.currentDropItem.child : [];
  106. } catch (e) {
  107. return [];
  108. }
  109. },
  110. getTextColor(index) {
  111. return index => {
  112. const active = this.currentDropItem.activeIndex == index;
  113. const color = this.currentDropItem.color;
  114. const activeColor = this.currentDropItem.activeColor;
  115. if (active) {
  116. return activeColor ? activeColor : '#3c9cff';
  117. }
  118. return color ? color : '#333';
  119. }
  120. },
  121. getTextSize(index) {
  122. return index => {
  123. const active = this.currentDropItem.activeIndex == index;
  124. const size = this.currentDropItem.size;
  125. const activeSize = this.currentDropItem.activeSize;
  126. if (active) {
  127. return activeSize ? activeSize : '30rpx';
  128. }
  129. return size ? size : '30rpx';
  130. }
  131. },
  132. itemCustomStyle() {
  133. return index => {
  134. const active = this.currentDropItem.activeIndex == index;
  135. const style = {};
  136. if (active && this.currentDropItem.itemActiveCustomStyle) {
  137. return this.$uv.deepMerge(style, this.$uv.addStyle(this.currentDropItem.itemActiveCustomStyle));
  138. }
  139. if (this.currentDropItem.itemCustomStyle) {
  140. return this.$uv.deepMerge(style, this.$uv.addStyle(this.currentDropItem.itemCustomStyle))
  141. }
  142. return style;
  143. }
  144. }
  145. },
  146. created() {
  147. this.init();
  148. },
  149. methods: {
  150. blockClick() {},
  151. clickHandler(item, index) {
  152. this.currentDropItem.activeIndex = index;
  153. this.$emit('clickItem', item);
  154. this.close();
  155. },
  156. init() {
  157. uni.$off(`${this.sign}_GETRECT`);
  158. uni.$on(`${this.sign}_GETRECT`, rect => {
  159. this.rect = rect;
  160. })
  161. uni.$off(`${this.sign}_CLICKMENU`);
  162. uni.$on(`${this.sign}_CLICKMENU`, async res => {
  163. if (res.show) {
  164. this.open();
  165. } else {
  166. this.close();
  167. }
  168. })
  169. },
  170. open() {
  171. this.show = true;
  172. this.$nextTick(async () => {
  173. // #ifndef H5 || MP-WEIXIN
  174. await this.$uv.sleep(60);
  175. // #endif
  176. const res = await this.queryRect();
  177. // #ifndef APP-NVUE
  178. this.height = res.height;
  179. // #endif
  180. // #ifdef APP-NVUE
  181. this.animation(res.height);
  182. // #endif
  183. this.$emit('popupChange', { show: true });
  184. })
  185. },
  186. close() {
  187. if(!this.show) return;
  188. this.height = 0;
  189. // #ifndef APP-NVUE
  190. this.height = 0;
  191. // #endif
  192. // #ifdef APP-NVUE
  193. this.animation(0);
  194. // #endif
  195. this.show = false;
  196. uni.$emit(`${this.sign}_CLOSEPOPUP`);
  197. this.$emit('popupChange', { show: false });
  198. },
  199. clickOverlay() {
  200. if (this.clickOverlayOnClose) {
  201. this.close();
  202. }
  203. },
  204. // 查询内容高度
  205. queryRect() {
  206. // #ifndef APP-NVUE
  207. // 组件内部一般用this.$uvGetRect,对外的为getRect,二者功能一致,名称不同
  208. return new Promise(resolve => {
  209. this.$uvGetRect(`.uv-dp__container__list`).then(size => {
  210. resolve(size)
  211. })
  212. })
  213. // #endif
  214. // #ifdef APP-NVUE
  215. // nvue下,使用dom模块查询元素高度
  216. // 返回一个promise,让调用此方法的主体能使用then回调
  217. return new Promise(resolve => {
  218. dom.getComponentRect(this.$refs.uvDPList, res => {
  219. resolve(res.size)
  220. })
  221. })
  222. // #endif
  223. },
  224. // nvue下设置高度
  225. animation(height, duration = 200) {
  226. // #ifdef APP-NVUE
  227. const ref = this.$refs['uvDPContainer'];
  228. animation.transition(ref, {
  229. styles: {
  230. height: `${height}px`
  231. },
  232. duration
  233. })
  234. // #endif
  235. }
  236. }
  237. }
  238. </script>
  239. <style scoped lang="scss">
  240. .uv-dp__container {
  241. /* #ifndef APP-NVUE */
  242. overflow: hidden;
  243. transition: all .15s;
  244. /* #endif */
  245. background-color: #fff;
  246. }
  247. .uv-dp__container__list {
  248. &--item {
  249. padding: 20rpx 60rpx;
  250. }
  251. }
  252. </style>