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.
 
 
 

260 regels
5.3 KiB

  1. <template>
  2. <view
  3. v-if="innerShow && pageList.length"
  4. class="up-guide"
  5. :style="{ zIndex: `${zIndex}` }"
  6. @touchmove.stop.prevent
  7. >
  8. <swiper
  9. class="up-guide__swiper"
  10. :current="current"
  11. @change="onSwiperChange"
  12. >
  13. <swiper-item v-for="(item, index) in pageList" :key="index">
  14. <view class="up-guide__page" :style="{ backgroundColor: item.backgroundColor || bgColor }">
  15. <template v-if="item.image">
  16. <image class="up-guide__image" :src="item.image" mode="aspectFit"></image>
  17. </template>
  18. <view v-else class="up-guide__placeholder">暂无引导图</view>
  19. <text v-if="item.title" class="up-guide__title">{{ item.title }}</text>
  20. <text v-if="item.desc" class="up-guide__desc">{{ item.desc }}</text>
  21. </view>
  22. </swiper-item>
  23. </swiper>
  24. <view class="up-guide__footer">
  25. <view v-if="indicator" class="up-guide__dots">
  26. <view
  27. v-for="(_, dotIndex) in pageList"
  28. :key="dotIndex"
  29. class="up-guide__dot"
  30. :class="{ 'up-guide__dot--active': dotIndex === current }"
  31. ></view>
  32. </view>
  33. <view class="up-guide__actions">
  34. <view v-if="showSkip" class="up-guide__btn up-guide__btn--ghost" @tap="onSkip">
  35. {{ skipText }}
  36. </view>
  37. <view class="up-guide__btn up-guide__btn--primary" @tap="onPrimaryAction">
  38. {{ isLastPage() ? finishText : nextText }}
  39. </view>
  40. </view>
  41. </view>
  42. </view>
  43. </template>
  44. <script>
  45. import { props } from './props';
  46. import { mpMixin } from '../../libs/mixin/mpMixin';
  47. import { mixin } from '../../libs/mixin/mixin';
  48. /**
  49. * Guide 首屏引导
  50. * @description 全屏首屏引导组件,支持一次性记忆与多页滑动
  51. */
  52. export default {
  53. name: 'up-guide',
  54. mixins: [mpMixin, mixin, props],
  55. emits: ['update:show', 'change', 'skip', 'finish', 'close'],
  56. data() {
  57. return {
  58. innerShow: false,
  59. current: 0,
  60. closing: false
  61. }
  62. },
  63. computed: {
  64. pageList() {
  65. return Array.isArray(this.list) ? this.list : []
  66. },
  67. resolvedStorageKey() {
  68. return this.storageKey || 'up-guide-default'
  69. }
  70. },
  71. watch: {
  72. show(value) {
  73. this.innerShow = !!value
  74. }
  75. },
  76. mounted() {
  77. this.bootstrap()
  78. },
  79. methods: {
  80. bootstrap() {
  81. if (!this.pageList.length) {
  82. if (process.env.NODE_ENV !== 'production') {
  83. console.warn('[up-guide] list is empty')
  84. }
  85. return
  86. }
  87. if (this.once && this.readRemembered()) {
  88. this.innerShow = false
  89. this.$emit('update:show', false)
  90. return
  91. }
  92. this.innerShow = !!this.show
  93. },
  94. isLastPage() {
  95. return this.current >= this.pageList.length - 1
  96. },
  97. onSwiperChange(event) {
  98. const next = Number(event?.detail?.current ?? 0)
  99. this.current = next
  100. this.$emit('change', { current: next })
  101. },
  102. onPrimaryAction() {
  103. if (this.isLastPage()) {
  104. this.$emit('finish')
  105. this.close(true)
  106. return
  107. }
  108. this.current += 1
  109. this.$emit('change', { current: this.current })
  110. },
  111. onSkip() {
  112. this.$emit('skip')
  113. this.close(true)
  114. },
  115. open() {
  116. this.current = 0
  117. this.innerShow = true
  118. this.$emit('update:show', true)
  119. },
  120. close(remember = true) {
  121. if (this.closing) return
  122. this.closing = true
  123. if (remember && this.once) {
  124. this.writeRemembered()
  125. }
  126. this.innerShow = false
  127. this.$emit('update:show', false)
  128. this.$emit('close')
  129. this.$nextTick(() => {
  130. this.closing = false
  131. })
  132. },
  133. reset() {
  134. try {
  135. uni.removeStorageSync(this.resolvedStorageKey)
  136. } catch (error) {}
  137. },
  138. readRemembered() {
  139. try {
  140. const value = uni.getStorageSync(this.resolvedStorageKey)
  141. return value === true || value === 1 || value === '1'
  142. } catch (error) {
  143. return false
  144. }
  145. },
  146. writeRemembered() {
  147. try {
  148. uni.setStorageSync(this.resolvedStorageKey, 1)
  149. } catch (error) {}
  150. }
  151. }
  152. }
  153. </script>
  154. <style lang="scss" scoped>
  155. .up-guide {
  156. position: fixed;
  157. left: 0;
  158. top: 0;
  159. right: 0;
  160. bottom: 0;
  161. display: flex;
  162. flex-direction: column;
  163. }
  164. .up-guide__swiper {
  165. flex: 1;
  166. }
  167. .up-guide__page {
  168. height: 100%;
  169. padding: 120rpx 40rpx 40rpx;
  170. box-sizing: border-box;
  171. display: flex;
  172. flex-direction: column;
  173. align-items: center;
  174. color: #ffffff;
  175. }
  176. .up-guide__image {
  177. width: 560rpx;
  178. height: 560rpx;
  179. }
  180. .up-guide__placeholder {
  181. width: 560rpx;
  182. height: 560rpx;
  183. border-radius: 24rpx;
  184. background: rgba(255, 255, 255, 0.12);
  185. display: flex;
  186. align-items: center;
  187. justify-content: center;
  188. }
  189. .up-guide__title {
  190. margin-top: 48rpx;
  191. font-size: 40rpx;
  192. font-weight: 600;
  193. }
  194. .up-guide__desc {
  195. margin-top: 18rpx;
  196. font-size: 28rpx;
  197. opacity: 0.85;
  198. text-align: center;
  199. }
  200. .up-guide__footer {
  201. padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
  202. }
  203. .up-guide__dots {
  204. display: flex;
  205. justify-content: center;
  206. gap: 12rpx;
  207. margin-bottom: 26rpx;
  208. }
  209. .up-guide__dot {
  210. width: 14rpx;
  211. height: 14rpx;
  212. border-radius: 999px;
  213. background: rgba(255, 255, 255, 0.35);
  214. }
  215. .up-guide__dot--active {
  216. width: 34rpx;
  217. background: #ffffff;
  218. }
  219. .up-guide__actions {
  220. display: flex;
  221. gap: 16rpx;
  222. }
  223. .up-guide__btn {
  224. flex: 1;
  225. height: 84rpx;
  226. border-radius: 42rpx;
  227. display: flex;
  228. align-items: center;
  229. justify-content: center;
  230. font-size: 28rpx;
  231. }
  232. .up-guide__btn--ghost {
  233. color: #ffffff;
  234. border: 2rpx solid rgba(255, 255, 255, 0.42);
  235. }
  236. .up-guide__btn--primary {
  237. color: #111111;
  238. background: #ffffff;
  239. font-weight: 600;
  240. }
  241. </style>