ConfigurableGuide.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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. import { BrandColors } from '../utils/brand-colors.js'
  38. export default {
  39. name: 'ConfigurableGuide',
  40. props: {
  41. popup: { type: Object, default: null },
  42. show: { type: Boolean, default: false }
  43. },
  44. data() {
  45. return {
  46. currentStep: 0
  47. }
  48. },
  49. computed: {
  50. isMulti() {
  51. return this.popup && this.popup.steps && this.popup.steps.length > 0
  52. },
  53. title() { return this.popup && this.popup.title ? this.popup.title : '' },
  54. description() { return this.popup && this.popup.description ? this.popup.description : '' },
  55. icon() { return this.popup && this.popup.icon ? this.popup.icon : '' },
  56. iconType() { return this.popup && this.popup.iconType !== undefined ? this.popup.iconType : 0 },
  57. themeColor() { return this.popup && this.popup.themeColor ? this.popup.themeColor : BrandColors.primary },
  58. steps() { return this.isMulti ? this.popup.steps : [] },
  59. currentStepObj() {
  60. if (!this.isMulti) return {}
  61. return this.steps[this.currentStep] || {}
  62. },
  63. btnLabel() {
  64. if (!this.isMulti) {
  65. return this.popup && this.popup.btnText ? this.popup.btnText : '知道了'
  66. }
  67. var isLast = this.currentStep >= this.steps.length - 1
  68. if (isLast) {
  69. return this.popup && this.popup.btnText ? this.popup.btnText : '开始体验'
  70. }
  71. return this.currentStepObj.btnText || '下一步'
  72. }
  73. },
  74. watch: {
  75. show(val) {
  76. if (val) this.currentStep = 0
  77. }
  78. },
  79. methods: {
  80. handleClick() {
  81. if (this.isMulti && this.currentStep < this.steps.length - 1) {
  82. this.currentStep++
  83. return
  84. }
  85. // 最后一步 / 单步:跳转 + 消费
  86. var action = ''
  87. if (this.isMulti) {
  88. action = this.currentStepObj.btnAction || this.popup.btnAction || ''
  89. } else {
  90. action = this.popup.btnAction || ''
  91. }
  92. this.$emit('done', { popup: this.popup, btnAction: action, buttonClicked: true })
  93. },
  94. handleClose() {
  95. this.$emit('done', { popup: this.popup, btnAction: '', buttonClicked: false })
  96. }
  97. }
  98. }
  99. </script>
  100. <style scoped lang="scss">
  101. .cg-overlay {
  102. position: fixed;
  103. top: 0;
  104. left: 0;
  105. width: 100%;
  106. height: 100%;
  107. background: rgba(0, 0, 0, 0.5);
  108. display: flex;
  109. align-items: center;
  110. justify-content: center;
  111. z-index: 9998;
  112. }
  113. .cg-card {
  114. width: 640rpx;
  115. min-height: 560rpx;
  116. background: $app-surface;
  117. border-radius: 32rpx;
  118. display: flex;
  119. flex-direction: column;
  120. align-items: center;
  121. overflow: hidden;
  122. }
  123. .cg-topbar { width: 100%; height: 12rpx; flex-shrink: 0; }
  124. .cg-content {
  125. flex: 1;
  126. display: flex;
  127. flex-direction: column;
  128. align-items: center;
  129. justify-content: center;
  130. padding: 72rpx 56rpx 40rpx;
  131. }
  132. .cg-icon { font-size: 80rpx; margin-bottom: 32rpx; display: block; }
  133. .cg-img { width: 160rpx; height: 160rpx; margin-bottom: 32rpx; }
  134. .cg-title {
  135. font-size: calc(36rpx * var(--font-scale, 1));
  136. font-weight: 700;
  137. color: $uni-text-color;
  138. text-align: center;
  139. margin-bottom: 20rpx;
  140. display: block;
  141. line-height: 1.3;
  142. }
  143. .cg-title--intro { font-size: calc(32rpx * var(--font-scale, 1)); font-weight: 800; }
  144. .cg-desc {
  145. font-size: calc(28rpx * var(--font-scale, 1));
  146. color: $uni-text-color-secondary;
  147. text-align: center;
  148. line-height: 1.6;
  149. display: block;
  150. padding: 0 16rpx;
  151. }
  152. .cg-dots { display: flex; align-items: center; justify-content: center; padding: 0 56rpx 32rpx; }
  153. .cg-dot { width: 14rpx; height: 14rpx; border-radius: 50%; background: $uni-border-color; margin: 0 8rpx; }
  154. .cg-dot--active { background: $brand-primary; transform: scale(1.3); }
  155. .cg-action { width: 100%; padding: 0 56rpx 48rpx; box-sizing: border-box; }
  156. .cg-btn {
  157. width: 100%;
  158. height: 88rpx;
  159. border-radius: 999rpx;
  160. color: $app-surface;
  161. font-size: 32rpx;
  162. font-weight: 700;
  163. display: flex;
  164. align-items: center;
  165. justify-content: center;
  166. }
  167. </style>