瑶都万能墙
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

247 lines
6.3 KiB

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