su-popover.vue 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. <template>
  2. <view class="ui-popover" :class="popover ? 'show' : 'hide'">
  3. <view
  4. class="ui-popover-button"
  5. :class="[ui]"
  6. :id="'popover-button-' + elId"
  7. :style="{ zIndex: index + zIndexConfig.popover }"
  8. @tap="popoverClick"
  9. @mouseleave="mouseleave"
  10. @mouseover="mouseover"
  11. >
  12. <slot></slot>
  13. </view>
  14. <view class="ui-popover-box" :style="BoxStyle">
  15. <view class="ui-popover-content-box" :id="'popover-content-' + elId" :style="contentStyle">
  16. <view
  17. class="ui-popover-content radius text-a"
  18. :class="bg"
  19. :style="{ zIndex: index + zIndexConfig.popover + 2 }"
  20. >
  21. <view class="p-3 text-sm" v-if="tips">{{ tips }}</view>
  22. <block v-else><slot name="content" /></block>
  23. </view>
  24. <view class="ui-popover-arrow" :class="bg" :style="arrowStyle"></view>
  25. </view>
  26. </view>
  27. <view
  28. class="ui-popover-mask"
  29. :class="mask ? 'bg-mask-50' : ''"
  30. :style="{ zIndex: index + zIndexConfig.popover - 1 }"
  31. @tap="popover = false"
  32. v-if="(popover && tips == '' && time == 0) || mask"
  33. ></view>
  34. </view>
  35. </template>
  36. <script>
  37. import { guid } from '@/sheep/helper';
  38. import zIndexConfig from '@/sheep/config/zIndex.js';
  39. import sheep from '@/sheep';
  40. export default {
  41. name: 'suPopover',
  42. data() {
  43. return {
  44. elId: guid(),
  45. zIndexConfig,
  46. popover: false,
  47. BoxStyle: '',
  48. contentStyle: '',
  49. arrowStyle: '',
  50. button: {},
  51. content: {},
  52. };
  53. },
  54. props: {
  55. ui: {
  56. type: String,
  57. default: '',
  58. },
  59. tips: {
  60. type: String,
  61. default: '',
  62. },
  63. bg: {
  64. type: String,
  65. default: 'ui-BG',
  66. },
  67. mask: {
  68. type: Boolean,
  69. default: false,
  70. },
  71. show: {
  72. type: [Boolean, String],
  73. default: 'change',
  74. },
  75. hover: {
  76. type: Boolean,
  77. default: false,
  78. },
  79. index: {
  80. type: Number,
  81. default: 0,
  82. },
  83. time: {
  84. type: Number,
  85. default: 0,
  86. },
  87. bottom: {
  88. type: Boolean,
  89. default: false,
  90. },
  91. isChange: {
  92. type: Boolean,
  93. default: false,
  94. },
  95. },
  96. watch: {
  97. popover(val) {
  98. this._computedQuery(
  99. sheep.$platform.device.windowWidth,
  100. sheep.$platform.device.windowHeight,
  101. );
  102. if (val) {
  103. if (this.tips != '' || this.time > 0) {
  104. setTimeout(
  105. () => {
  106. this.popover = false;
  107. },
  108. this.time == 0 ? 3000 : this.time,
  109. );
  110. }
  111. this.sys_layer = this.sys_layer + 100;
  112. } else {
  113. this.sys_layer = this.sys_layer - 100;
  114. }
  115. this.$emit('update:show', val);
  116. },
  117. show(val) {
  118. this.popover = val;
  119. },
  120. },
  121. mounted() {
  122. this.$nextTick(() => {
  123. this._computedQuery(
  124. sheep.$platform.device.windowWidth,
  125. sheep.$platform.device.windowHeight,
  126. );
  127. // #ifdef H5
  128. uni.onWindowResize((res) => {
  129. this._computedQuery(res.size.windowWidth, res.size.windowHeight);
  130. });
  131. // #endif
  132. });
  133. },
  134. methods: {
  135. _onHide() {
  136. this.popover = false;
  137. },
  138. _computedQuery(w, h) {
  139. uni
  140. .createSelectorQuery()
  141. .in(this)
  142. .select('#popover-button-' + this.elId)
  143. .boundingClientRect((button) => {
  144. if (button != null) {
  145. this.button = button;
  146. } else {
  147. console.log('popover-button-' + this.elId + ' data error');
  148. }
  149. })
  150. .select('#popover-content-' + this.elId)
  151. .boundingClientRect((content) => {
  152. if (content != null) {
  153. this.content = content;
  154. let button = this.button;
  155. //contentStyle
  156. let contentStyle = '';
  157. let arrowStyle = '';
  158. this.BoxStyle = `width:${w}px; left:-${button.left}px;z-index: ${
  159. this.index + this.sys_layer + 102
  160. }`;
  161. // 判断气泡在上面还是下面
  162. if (button.bottom < h / 2 || this.bottom) {
  163. // '下';
  164. contentStyle = contentStyle + `top:10px;`;
  165. arrowStyle = arrowStyle + `top:${-5}px;`;
  166. } else {
  167. // '上';
  168. contentStyle = contentStyle + `bottom:${button.height + 10}px;`;
  169. arrowStyle = arrowStyle + `bottom:${-5}px;`;
  170. }
  171. // 判断气泡箭头在左中右
  172. let btnCenter = button.right - button.width / 2;
  173. let contentCenter = content.right - content.width / 2;
  174. if (
  175. (btnCenter < w / 3 && content.width > btnCenter) ||
  176. (content.width > w / 2 && btnCenter < w / 2)
  177. ) {
  178. // '左';
  179. contentStyle = contentStyle + `left:10px;`;
  180. arrowStyle = arrowStyle + `left:${btnCenter - 17}px;`;
  181. } else if (
  182. (btnCenter > (w / 6) * 4 && content.width > w - btnCenter) ||
  183. (content.width > w / 2 && btnCenter > w / 2)
  184. ) {
  185. // '右';
  186. contentStyle = contentStyle + `right:10px;`;
  187. arrowStyle = arrowStyle + `right:${w - btnCenter - 17}px;`;
  188. } else {
  189. // '中';
  190. contentStyle =
  191. contentStyle + `left:${button.left - content.width / 2 + button.width / 2}px;`;
  192. arrowStyle = arrowStyle + `left:0px;right:0px;margin:auto;`;
  193. }
  194. this.arrowStyle = arrowStyle + `z-index:${this.index + this.sys_layer + 1};`;
  195. this.contentStyle = contentStyle + `z-index:${this.index + this.sys_layer + 2};`;
  196. } else {
  197. console.log('popover-content-' + this.elId + ' data error');
  198. }
  199. })
  200. .exec();
  201. },
  202. popoverClick() {
  203. if (this.isChange) {
  204. return false;
  205. }
  206. if (this.tips == '') {
  207. this.popover = !this.popover;
  208. } else {
  209. this.popover = true;
  210. }
  211. },
  212. mouseover() {
  213. if (this.hover && (this.tips != '' || this.content.height != 0)) {
  214. this.popover = true;
  215. }
  216. },
  217. mouseleave() {
  218. if (this.hover) {
  219. this.popover = false;
  220. }
  221. },
  222. },
  223. };
  224. </script>
  225. <style lang="scss">
  226. .ui-popover {
  227. position: relative;
  228. .ui-popover-button {
  229. position: relative;
  230. }
  231. .ui-popover-box {
  232. position: absolute;
  233. .ui-popover-content-box {
  234. position: absolute;
  235. .ui-popover-content {
  236. position: relative;
  237. }
  238. .ui-popover-arrow {
  239. position: absolute;
  240. height: 15px;
  241. width: 15px;
  242. border-radius: 2px;
  243. transform: rotate(45deg);
  244. }
  245. &::after {
  246. content: '';
  247. width: 100%;
  248. height: 110%;
  249. position: absolute;
  250. background-color: #000000;
  251. top: 5%;
  252. left: 0;
  253. filter: blur(15px);
  254. opacity: 0.15;
  255. }
  256. }
  257. }
  258. .ui-popover-mask {
  259. position: fixed;
  260. width: 100%;
  261. height: 100%;
  262. top: 0;
  263. left: 0;
  264. }
  265. &.show {
  266. .ui-popover-button {
  267. }
  268. .ui-popover-content-box {
  269. opacity: 1;
  270. pointer-events: auto;
  271. }
  272. .ui-popover-arrow {
  273. display: block;
  274. }
  275. .ui-popover-mask {
  276. display: block;
  277. }
  278. }
  279. &.hide {
  280. .ui-popover-button {
  281. }
  282. .ui-popover-content-box {
  283. opacity: 0;
  284. pointer-events: none;
  285. }
  286. .ui-popover-arrow {
  287. display: none;
  288. }
  289. .ui-popover-mask {
  290. display: none;
  291. }
  292. }
  293. }
  294. </style>