intent-picker.vue 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. <template>
  2. <view class="ip-mask" v-if="show" @click="onMaskTap">
  3. <view class="ip-panel" @click.stop>
  4. <view class="ip-header">
  5. <text class="ip-title">告诉我,你想怎么开始?</text>
  6. <text class="ip-sub">浠艾福为你定制专属家庭成长方案</text>
  7. </view>
  8. <!-- A 随便看看 -->
  9. <view class="ip-card" @click="emitSelect('A')">
  10. <view class="ip-card-icon" style="background: rgba(249,115,22,0.12);"><text class="ip-card-emoji">🎯</text></view>
  11. <view class="ip-card-body">
  12. <text class="ip-card-title">随便看看</text>
  13. <text class="ip-card-desc">先到处逛逛,了解浠艾福和各式服务</text>
  14. </view>
  15. <text class="ip-card-arrow">›</text>
  16. </view>
  17. <!-- B 问卷 -->
  18. <view class="ip-card" @click="emitSelect('B')">
  19. <view class="ip-card-icon" style="background: rgba(14,165,233,0.12);"><text class="ip-card-emoji">📋</text></view>
  20. <view class="ip-card-body">
  21. <text class="ip-card-title">智能问卷</text>
  22. <text class="ip-card-desc">先答几道题,系统为你匹配推荐方案</text>
  23. </view>
  24. <text class="ip-card-arrow">›</text>
  25. </view>
  26. <!-- C 问题域 -->
  27. <view class="ip-card" @click="onDomainTap">
  28. <view class="ip-card-icon" style="background: rgba(16,185,129,0.12);"><text class="ip-card-emoji">🔍</text></view>
  29. <view class="ip-card-body">
  30. <text class="ip-card-title">解决问题</text>
  31. <text class="ip-card-desc">选择你关心的问题,获得完整解决方案</text>
  32. </view>
  33. <text class="ip-card-arrow">›</text>
  34. </view>
  35. <!-- C 子级:问题域列表 -->
  36. <view class="ip-domain-wrap" v-if="showDomains">
  37. <view
  38. v-for="d in domains"
  39. :key="getKey(d)"
  40. class="ip-domain-item"
  41. :class="{ 'ip-domain-item-active': d.code === selectedCode }"
  42. @click="selectDomain(d)">
  43. <text class="ip-domain-icon">{{ d.icon || '🧭' }}</text>
  44. <text class="ip-domain-name">{{ d.name }}</text>
  45. <text class="ip-domain-check" v-if="d.code === selectedCode">✓</text>
  46. </view>
  47. <view class="ip-domain-confirm" @click="confirmDomain">
  48. <text class="ip-domain-confirm-text">开始我的方案</text>
  49. </view>
  50. </view>
  51. <view class="ip-skip" @click="onSkip">
  52. <text class="ip-skip-text">暂时跳过</text>
  53. </view>
  54. </view>
  55. </view>
  56. </template>
  57. <script>
  58. import { getProblemDomainList } from '../utils/api.js'
  59. export default {
  60. name: 'IntentPicker',
  61. props: {
  62. show: {
  63. type: Boolean,
  64. default: false
  65. }
  66. },
  67. data() {
  68. return {
  69. showDomains: false,
  70. domains: [],
  71. selectedCode: '',
  72. selectedName: '',
  73. loading: false
  74. }
  75. },
  76. watch: {
  77. show(val) {
  78. if (val) {
  79. this.showDomains = false
  80. this.selectedCode = ''
  81. this.selectedName = ''
  82. this.loadDomains()
  83. }
  84. }
  85. },
  86. methods: {
  87. getKey(d) {
  88. return d.id || d.code
  89. },
  90. loadDomains() {
  91. if (this.domains.length > 0 || this.loading) return
  92. this.loading = true
  93. var self = this
  94. getProblemDomainList().then(function(res) {
  95. self.loading = false
  96. var list = res && res.data
  97. self.domains = Array.isArray(list) ? list : []
  98. }).catch(function() {
  99. self.loading = false
  100. self.domains = []
  101. })
  102. },
  103. onMaskTap() {
  104. // 点击遮罩不关闭,防止误触跳过
  105. },
  106. onDomainTap() {
  107. this.showDomains = !this.showDomains
  108. this.loadDomains()
  109. },
  110. selectDomain(d) {
  111. this.selectedCode = d.code
  112. this.selectedName = d.name
  113. },
  114. confirmDomain() {
  115. if (!this.selectedCode) {
  116. uni.showToast({ title: '请先选择一个关注的问题', icon: 'none' })
  117. return
  118. }
  119. this.$emit('select', {
  120. intentType: 'C',
  121. problemDomainCode: this.selectedCode,
  122. problemDomainName: this.selectedName
  123. })
  124. },
  125. emitSelect(intentType) {
  126. this.$emit('select', { intentType: intentType })
  127. },
  128. onSkip() {
  129. this.$emit('close')
  130. }
  131. }
  132. }
  133. </script>
  134. <style scoped>
  135. .ip-mask {
  136. position: fixed;
  137. top: 0;
  138. left: 0;
  139. right: 0;
  140. bottom: 0;
  141. background: rgba(0, 0, 0, 0.55);
  142. display: flex;
  143. align-items: flex-end;
  144. justify-content: center;
  145. z-index: 9999;
  146. }
  147. .ip-panel {
  148. width: 100%;
  149. background: #FFF7ED;
  150. border-radius: 32rpx 32rpx 0 0;
  151. padding: 48rpx 40rpx 60rpx;
  152. box-sizing: border-box;
  153. max-height: 88vh;
  154. overflow-y: auto;
  155. }
  156. .ip-header {
  157. margin-bottom: 32rpx;
  158. }
  159. .ip-title {
  160. display: block;
  161. font-size: 38rpx;
  162. font-weight: bold;
  163. color: #333;
  164. text-align: center;
  165. }
  166. .ip-sub {
  167. display: block;
  168. font-size: 26rpx;
  169. color: #999;
  170. text-align: center;
  171. margin-top: 12rpx;
  172. }
  173. .ip-card {
  174. display: flex;
  175. align-items: center;
  176. background: #fff;
  177. border-radius: 20rpx;
  178. padding: 28rpx 30rpx;
  179. margin-bottom: 20rpx;
  180. box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
  181. }
  182. .ip-card:active {
  183. transform: scale(0.98);
  184. }
  185. .ip-card-icon {
  186. width: 88rpx;
  187. height: 88rpx;
  188. border-radius: 22rpx;
  189. display: flex;
  190. align-items: center;
  191. justify-content: center;
  192. margin-right: 24rpx;
  193. flex-shrink: 0;
  194. }
  195. .ip-card-emoji {
  196. font-size: 44rpx;
  197. }
  198. .ip-card-body {
  199. flex: 1;
  200. }
  201. .ip-card-title {
  202. display: block;
  203. font-size: 30rpx;
  204. font-weight: 600;
  205. color: #333;
  206. }
  207. .ip-card-desc {
  208. display: block;
  209. font-size: 24rpx;
  210. color: #999;
  211. margin-top: 6rpx;
  212. }
  213. .ip-card-arrow {
  214. font-size: 44rpx;
  215. color: #CCC;
  216. margin-left: 12rpx;
  217. }
  218. .ip-domain-wrap {
  219. background: #fff;
  220. border-radius: 20rpx;
  221. padding: 24rpx;
  222. margin-bottom: 20rpx;
  223. box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
  224. }
  225. .ip-domain-item {
  226. display: flex;
  227. align-items: center;
  228. padding: 20rpx 16rpx;
  229. border-radius: 16rpx;
  230. border: 2rpx solid transparent;
  231. margin-bottom: 12rpx;
  232. }
  233. .ip-domain-item:last-child {
  234. margin-bottom: 0;
  235. }
  236. .ip-domain-item-active {
  237. border-color: #F97316;
  238. background: #FFF7ED;
  239. }
  240. .ip-domain-icon {
  241. font-size: 36rpx;
  242. margin-right: 16rpx;
  243. }
  244. .ip-domain-name {
  245. flex: 1;
  246. font-size: 28rpx;
  247. color: #333;
  248. }
  249. .ip-domain-check {
  250. color: #F97316;
  251. font-size: 32rpx;
  252. font-weight: bold;
  253. }
  254. .ip-domain-confirm {
  255. margin-top: 16rpx;
  256. background: linear-gradient(135deg, #F97316, #FB923C);
  257. border-radius: 40rpx;
  258. padding: 22rpx 0;
  259. text-align: center;
  260. }
  261. .ip-domain-confirm:active {
  262. opacity: 0.9;
  263. }
  264. .ip-domain-confirm-text {
  265. color: #fff;
  266. font-size: 30rpx;
  267. font-weight: 600;
  268. }
  269. .ip-skip {
  270. text-align: center;
  271. margin-top: 20rpx;
  272. padding: 16rpx 0;
  273. }
  274. .ip-skip-text {
  275. font-size: 26rpx;
  276. color: #999;
  277. text-decoration: underline;
  278. }
  279. </style>