ConfigurableGuide.vue 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. <template>
  2. <view class="cg-overlay" v-if="show">
  3. <view class="cg-card">
  4. <!-- 顶部主题色条 -->
  5. <view class="cg-topbar" :style="{ background: themeColor }"></view>
  6. <!-- 步骤内容 -->
  7. <view class="cg-content">
  8. <!-- 单步弹窗:无 steps 时直接用 title/description/icon -->
  9. <block v-if="!isMulti">
  10. <text v-if="iconType === 0 && icon" class="cg-icon">{{ icon }}</text>
  11. <image v-if="iconType === 1 && icon" class="cg-img" :src="icon" mode="aspectFit" />
  12. <text class="cg-title">{{ title }}</text>
  13. <text class="cg-desc">{{ description }}</text>
  14. </block>
  15. <!-- 多步向导:steps 数组渲染 -->
  16. <block v-else>
  17. <text v-if="currentStepObj.iconType === 0 && currentStepObj.icon" class="cg-icon">{{ currentStepObj.icon }}</text>
  18. <image v-if="currentStepObj.iconType === 1 && currentStepObj.icon" class="cg-img" :src="currentStepObj.icon" mode="aspectFit" />
  19. <text class="cg-title" :class="{ 'cg-title--intro': currentStepObj.isIntro }">{{ currentStepObj.title }}</text>
  20. <text class="cg-desc">{{ currentStepObj.desc }}</text>
  21. </block>
  22. </view>
  23. <!-- 步进器(仅多步) -->
  24. <view class="cg-dots" v-if="isMulti">
  25. <view v-for="(s, i) in steps" :key="i" class="cg-dot" :class="{ 'cg-dot--active': i === currentStep }"></view>
  26. </view>
  27. <!-- 按钮 -->
  28. <view class="cg-action">
  29. <view class="cg-btn" :style="{ background: themeColor }" @click="handleClick">
  30. <text>{{ btnLabel }}</text>
  31. </view>
  32. </view>
  33. </view>
  34. </view>
  35. </template>
  36. <script>
  37. export default {
  38. name: 'ConfigurableGuide',
  39. props: {
  40. popup: { type: Object, default: null },
  41. show: { type: Boolean, default: false }
  42. },
  43. data() {
  44. return {
  45. currentStep: 0
  46. }
  47. },
  48. computed: {
  49. isMulti() {
  50. return this.popup && this.popup.steps && this.popup.steps.length > 0
  51. },
  52. title() { return this.popup && this.popup.title ? this.popup.title : '' },
  53. description() { return this.popup && this.popup.description ? this.popup.description : '' },
  54. icon() { return this.popup && this.popup.icon ? this.popup.icon : '' },
  55. iconType() { return this.popup && this.popup.iconType !== undefined ? this.popup.iconType : 0 },
  56. themeColor() { return this.popup && this.popup.themeColor ? this.popup.themeColor : '#F97316' },
  57. steps() { return this.isMulti ? this.popup.steps : [] },
  58. currentStepObj() {
  59. if (!this.isMulti) return {}
  60. return this.steps[this.currentStep] || {}
  61. },
  62. btnLabel() {
  63. if (!this.isMulti) {
  64. return this.popup && this.popup.btnText ? this.popup.btnText : '知道了'
  65. }
  66. var isLast = this.currentStep >= this.steps.length - 1
  67. if (isLast) {
  68. return this.popup && this.popup.btnText ? this.popup.btnText : '开始体验'
  69. }
  70. return this.currentStepObj.btnText || '下一步'
  71. }
  72. },
  73. watch: {
  74. show(val) {
  75. if (val) this.currentStep = 0
  76. }
  77. },
  78. methods: {
  79. handleClick() {
  80. if (this.isMulti && this.currentStep < this.steps.length - 1) {
  81. this.currentStep++
  82. return
  83. }
  84. // 最后一步 / 单步:跳转 + 消费
  85. var action = ''
  86. if (this.isMulti) {
  87. action = this.currentStepObj.btnAction || this.popup.btnAction || ''
  88. } else {
  89. action = this.popup.btnAction || ''
  90. }
  91. this.$emit('done', { popup: this.popup, btnAction: action, buttonClicked: true })
  92. },
  93. handleClose() {
  94. this.$emit('done', { popup: this.popup, btnAction: '', buttonClicked: false })
  95. }
  96. }
  97. }
  98. </script>
  99. <style scoped>
  100. .cg-overlay {
  101. position: fixed;
  102. top: 0;
  103. left: 0;
  104. width: 100%;
  105. height: 100%;
  106. background: rgba(0, 0, 0, 0.5);
  107. display: flex;
  108. align-items: center;
  109. justify-content: center;
  110. z-index: 9998;
  111. }
  112. .cg-card {
  113. width: 640rpx;
  114. min-height: 560rpx;
  115. background: #FFFFFF;
  116. border-radius: 32rpx;
  117. display: flex;
  118. flex-direction: column;
  119. align-items: center;
  120. overflow: hidden;
  121. }
  122. .cg-topbar { width: 100%; height: 12rpx; flex-shrink: 0; }
  123. .cg-content {
  124. flex: 1;
  125. display: flex;
  126. flex-direction: column;
  127. align-items: center;
  128. justify-content: center;
  129. padding: 72rpx 56rpx 40rpx;
  130. }
  131. .cg-icon { font-size: 80rpx; margin-bottom: 32rpx; display: block; }
  132. .cg-img { width: 160rpx; height: 160rpx; margin-bottom: 32rpx; }
  133. .cg-title {
  134. font-size: 36rpx;
  135. font-weight: 700;
  136. color: #1E293B;
  137. text-align: center;
  138. margin-bottom: 20rpx;
  139. display: block;
  140. line-height: 1.3;
  141. }
  142. .cg-title--intro { font-size: 32rpx; font-weight: 800; }
  143. .cg-desc {
  144. font-size: 28rpx;
  145. color: #64748B;
  146. text-align: center;
  147. line-height: 1.6;
  148. display: block;
  149. padding: 0 16rpx;
  150. }
  151. .cg-dots { display: flex; align-items: center; justify-content: center; padding: 0 56rpx 32rpx; }
  152. .cg-dot { width: 14rpx; height: 14rpx; border-radius: 50%; background: #CBD5E1; margin: 0 8rpx; }
  153. .cg-dot--active { background: #F97316; transform: scale(1.3); }
  154. .cg-action { width: 100%; padding: 0 56rpx 48rpx; box-sizing: border-box; }
  155. .cg-btn {
  156. width: 100%;
  157. height: 88rpx;
  158. border-radius: 999rpx;
  159. color: #FFFFFF;
  160. font-size: 32rpx;
  161. font-weight: 700;
  162. display: flex;
  163. align-items: center;
  164. justify-content: center;
  165. }
  166. </style>