fs-popover.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. <template>
  2. <view class="fs-popover">
  3. <view class="fs-popover-refer" @click="handleClickRefer">
  4. <slot name="refer"></slot>
  5. </view>
  6. <view class="fs-popover-action" v-if="actionVisible" :class="['fs-popover-' + placement]" @click="handleClose">
  7. <view class="fs-popover-arrow"></view>
  8. <slot>
  9. <view class="fs-popover-action-item line1" v-for="(item, index) in actions" :key="index" @click="handleClickAction(item)">{{item.text}}</view>
  10. </slot>
  11. </view>
  12. <fs-mask v-model="actionVisible" :z-index="100" bgColor="rgba(0,0,0,0)"></fs-mask>
  13. </view>
  14. </template>
  15. <script>
  16. export default {
  17. name:"fs-popover",
  18. }
  19. </script>
  20. <script setup>
  21. import { ref, onMounted } from 'vue'
  22. import utils from '@/utils/utils'
  23. const props = defineProps({
  24. placement: {
  25. type: String,
  26. default: 'bottom',
  27. validator(value) {
  28. return ['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end','left', 'left-start', 'left-end','right', 'right-start', 'right-end'].includes(value)
  29. }
  30. },
  31. actions: Array,
  32. bgColor: {
  33. type: String,
  34. default: '#fff'
  35. },
  36. textColor: {
  37. type: String,
  38. default: '#666'
  39. },
  40. borderColor: {
  41. type: String,
  42. default: '#E8EAF2'
  43. },
  44. zIndex: {
  45. type: Number,
  46. default: 99
  47. }
  48. })
  49. const emits = defineEmits(['clickAction'])
  50. const uuid = utils.uuid()
  51. const actionVisible = ref(false)
  52. const handleClickRefer = () => {
  53. uni.$emit('popoverClose', { uuid: uuid })
  54. actionVisible.value = !actionVisible.value
  55. }
  56. const handleClickAction = item => {
  57. emits('clickAction', item)
  58. }
  59. const handleClose = () => {
  60. actionVisible.value = false
  61. }
  62. uni.$on('popoverClose', res => {
  63. if (uuid !== res.uuid) {
  64. handleClose()
  65. }
  66. })
  67. defineExpose({
  68. handleClose
  69. })
  70. </script>
  71. <style lang="scss" scoped>
  72. $margin: 20rpx;
  73. $arrowOffset: 30rpx;
  74. .fs-popover{
  75. display: inline-block;
  76. position: relative;
  77. z-index: v-bind(zIndex);
  78. &-action{
  79. position: absolute;
  80. min-width: 260rpx;
  81. z-index: calc(v-bind(zIndex) + 1);
  82. background-color: v-bind(bgColor);
  83. transition: opacity .15s,transform .15s;
  84. border-radius: 12rpx;
  85. padding: 0 30rpx;
  86. color: v-bind(textColor);
  87. &-item{
  88. padding: 20rpx;
  89. & + &{
  90. border-top: 2rpx solid v-bind(borderColor);
  91. }
  92. }
  93. }
  94. &-arrow{
  95. position: absolute;
  96. width: 0;
  97. height: 0;
  98. border-color: transparent;
  99. border-style: solid;
  100. border-width: 12rpx;
  101. }
  102. &-top{
  103. left: 50%;
  104. bottom: 100%;
  105. transform: translateX(-50%);
  106. margin-bottom: $margin;
  107. .fs-popover-arrow{
  108. border-bottom-width: 0;
  109. border-top-color: v-bind(bgColor);
  110. bottom: 0;
  111. left: 50%;
  112. transform: translate(-50%, 100%);
  113. }
  114. }
  115. &-top-start{
  116. left: 0;
  117. bottom: 100%;
  118. margin-bottom: $margin;
  119. .fs-popover-arrow{
  120. border-bottom-width: 0;
  121. border-top-color: v-bind(bgColor);
  122. bottom: 0;
  123. left: $arrowOffset;
  124. transform: translateY(100%);
  125. }
  126. }
  127. &-top-end{
  128. right: 0;
  129. bottom: 100%;
  130. margin-bottom: $margin;
  131. .fs-popover-arrow{
  132. border-bottom-width: 0;
  133. border-top-color: v-bind(bgColor);
  134. bottom: 0;
  135. right: $arrowOffset;
  136. transform: translateY(100%);
  137. }
  138. }
  139. &-bottom{
  140. left: 50%;
  141. top: 100%;
  142. transform: translateX(-50%);
  143. margin-top: $margin;
  144. .fs-popover-arrow{
  145. border-top-width: 0;
  146. border-bottom-color: v-bind(bgColor);
  147. top: 0;
  148. left: 50%;
  149. transform: translate(-50%, -100%);
  150. }
  151. }
  152. &-bottom-start{
  153. left: 0;
  154. top: 100%;
  155. margin-top: $margin;
  156. .fs-popover-arrow{
  157. border-top-width: 0;
  158. border-bottom-color: v-bind(bgColor);
  159. top: 0;
  160. left: $arrowOffset;
  161. transform: translateY(-100%);
  162. }
  163. }
  164. &-bottom-end{
  165. right: 0;
  166. top: 100%;
  167. margin-top: $margin;
  168. .fs-popover-arrow{
  169. border-top-width: 0;
  170. border-bottom-color: v-bind(bgColor);
  171. top: 0;
  172. right: $arrowOffset;
  173. transform: translateY(-100%);
  174. }
  175. }
  176. &-left{
  177. left: -$margin;
  178. top: 50%;
  179. transform: translate(-100%, -50%);
  180. .fs-popover-arrow{
  181. border-right-width: 0;
  182. border-left-color: v-bind(bgColor);
  183. top: 50%;
  184. right: 0;
  185. transform: translate(100%, -50%);
  186. }
  187. }
  188. &-left-start{
  189. left: -$margin;
  190. top: 0;
  191. transform: translateX(-100%);
  192. .fs-popover-arrow{
  193. border-right-width: 0;
  194. border-left-color: v-bind(bgColor);
  195. top: $arrowOffset;
  196. right: 0;
  197. transform: translateX(100%);
  198. }
  199. }
  200. &-left-end{
  201. left: -$margin;
  202. bottom: 0;
  203. transform: translateX(-100%);
  204. .fs-popover-arrow{
  205. border-right-width: 0;
  206. border-left-color: v-bind(bgColor);
  207. bottom: $arrowOffset;
  208. right: 0;
  209. transform: translateX(100%);
  210. }
  211. }
  212. &-right{
  213. left: 100%;
  214. top: 0;
  215. transform: translateY(-50%);
  216. margin-left: $margin;
  217. .fs-popover-arrow{
  218. border-left-width: 0;
  219. border-right-color: v-bind(bgColor);
  220. top: 50%;
  221. left: 0;
  222. transform: translateX(-50%);
  223. }
  224. }
  225. &-right-start{
  226. left: 100%;
  227. top: 0;
  228. margin-left: $margin;
  229. .fs-popover-arrow{
  230. border-left-width: 0;
  231. border-right-color: v-bind(bgColor);
  232. top: $arrowOffset;
  233. left: 0;
  234. transform: translateX(-100%);
  235. }
  236. }
  237. &-right-end{
  238. left: 100%;
  239. bottom: 0;
  240. margin-left: $margin;
  241. .fs-popover-arrow{
  242. border-left-width: 0;
  243. border-right-color: v-bind(bgColor);
  244. left: 0;
  245. bottom: $arrowOffset;
  246. transform: translateX(-100%);
  247. }
  248. }
  249. }
  250. </style>