teacher-service.vue 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. <template>
  2. <view class="ts-page">
  3. <!-- 提示 -->
  4. <view class="ts-tip">
  5. <text class="ts-tip-icon">👩‍🏫</text>
  6. <text class="ts-tip-text">选择规划师服务,专业团队为您审核和定制健康方案</text>
  7. </view>
  8. <!-- 服务类型切换 -->
  9. <view class="ts-type-bar">
  10. <view class="ts-type-item" :class="{ active: serviceType === 'online' }" @click="serviceType = 'online'">
  11. <text class="ts-type-icon">💻</text>
  12. <text class="ts-type-label">纯线上服务</text>
  13. </view>
  14. <view class="ts-type-item" :class="{ active: serviceType === 'contact' }" @click="serviceType = 'contact'">
  15. <text class="ts-type-icon">📱</text>
  16. <text class="ts-type-label">可联系服务</text>
  17. </view>
  18. </view>
  19. <!-- 服务说明 -->
  20. <view class="ts-info-card" v-if="serviceType === 'online'">
  21. <text class="ts-info-title">纯线上服务</text>
  22. <text class="ts-info-desc">• 规划师通过小程序内消息与您沟通</text>
  23. <text class="ts-info-desc">• 双方不见面,不单独微信/电话联系</text>
  24. <text class="ts-info-desc">• 所有沟通记录可追溯</text>
  25. <text class="ts-info-price">费用包含:方案审核、定制调整、30天在线咨询</text>
  26. <text class="ts-info-refund">退费说明:规划师审核前可全额退款;方案确认后不支持退款</text>
  27. </view>
  28. <view class="ts-info-card" v-else>
  29. <text class="ts-info-title">可联系服务</text>
  30. <text class="ts-info-desc">• 规划师可通过微信、电话与您联系</text>
  31. <text class="ts-info-desc">• 购买后可查看规划师联系方式</text>
  32. <text class="ts-info-desc">• 支持一对一深度沟通</text>
  33. <text class="ts-info-price">费用包含:方案审核、定制调整、90天专属顾问、微信/电话沟通</text>
  34. <text class="ts-info-refund">退费说明:规划师联系前可全额退款;首次沟通后不支持退款</text>
  35. </view>
  36. <!-- 规划师列表 -->
  37. <view class="ts-list" v-if="guides.length > 0">
  38. <view v-for="(g, idx) in guides" :key="idx"
  39. class="ts-card" :class="{ active: selectedGuideId === g.id }"
  40. @click="selectGuide(g)">
  41. <view class="ts-card-top">
  42. <view class="ts-avatar">{{ (g.name || '规划师').charAt(0) }}</view>
  43. <view class="ts-card-info">
  44. <text class="ts-card-name">{{ g.name || '规划师' }}</text>
  45. <text class="ts-card-spec" v-if="g.specialty">{{ g.specialty }}</text>
  46. <text class="ts-card-tag" v-if="g.isBound">已绑定家庭</text>
  47. </view>
  48. <view class="ts-card-check">{{ selectedGuideId === g.id ? '✓' : '' }}</view>
  49. </view>
  50. <view class="ts-card-desc" v-if="g.description">
  51. <text>{{ g.description }}</text>
  52. </view>
  53. <view class="ts-card-price" v-if="g.price">
  54. <text class="ts-price-val">¥{{ g.price }}</text>
  55. <text class="ts-price-unit">/ {{ serviceType === 'online' ? '30天' : '90天' }}</text>
  56. </view>
  57. </view>
  58. </view>
  59. <!-- 空状态 -->
  60. <view class="ts-empty" v-if="!loading && guides.length === 0">
  61. <text class="ts-empty-icon">🔍</text>
  62. <text class="ts-empty-text">暂无可用规划师</text>
  63. <text class="ts-empty-hint">请联系管理员为您分配规划师</text>
  64. </view>
  65. <!-- 加载中 -->
  66. <view class="ts-loading" v-if="loading">
  67. <text class="ts-loading-text">加载中...</text>
  68. </view>
  69. <!-- 底部按钮 -->
  70. <view class="ts-bottom-bar">
  71. <button class="ts-btn-submit" :disabled="!selectedGuideId || submitting"
  72. @click="confirmSelection">
  73. {{ submitting ? '提交中...' : (selectedGuideId ? '确认选择并支付' : '请选择规划师') }}
  74. </button>
  75. </view>
  76. </view>
  77. </template>
  78. <script>
  79. import { getAvailableTeachers } from '../../utils/api'
  80. export default {
  81. data: function() {
  82. return {
  83. guides: [],
  84. selectedGuideId: null,
  85. serviceType: 'online',
  86. familyId: null,
  87. planId: null,
  88. memberIds: '',
  89. goal: '',
  90. loading: false,
  91. submitting: false
  92. }
  93. },
  94. onLoad: function(options) {
  95. this.familyId = options.familyId ? parseInt(options.familyId) : null
  96. this.planId = options.planId ? parseInt(options.planId) : null
  97. this.memberIds = options.memberIds || ''
  98. this.goal = options.goal || ''
  99. if (!this.familyId) {
  100. this.familyId = uni.getStorageSync('familyId') ? parseInt(uni.getStorageSync('familyId')) : null
  101. }
  102. if (!this.familyId) {
  103. uni.showToast({ title: '缺少家庭信息', icon: 'none' })
  104. setTimeout(function() { uni.navigateBack() }, 1000)
  105. return
  106. }
  107. this.loadGuides()
  108. },
  109. methods: {
  110. loadGuides: function() {
  111. var self = this
  112. self.loading = true
  113. getAvailableTeachers(self.familyId).then(function(res) {
  114. self.loading = false
  115. if (res && res.code === 200 && res.data) {
  116. var list = res.data instanceof Array ? res.data : []
  117. self.guides = list
  118. } else {
  119. self.guides = []
  120. }
  121. }).catch(function() {
  122. self.loading = false
  123. self.guides = []
  124. })
  125. },
  126. selectGuide: function(g) {
  127. this.selectedGuideId = g.id
  128. },
  129. confirmSelection: function() {
  130. var self = this
  131. if (!self.selectedGuideId || self.submitting) return
  132. self.submitting = true
  133. // 将选择保存到本地,返回上一页时 health-plan-summary 会读取
  134. uni.setStorageSync('healthPlan_teacherId', self.selectedGuideId)
  135. uni.setStorageSync('healthPlan_serviceType', self.serviceType)
  136. uni.showToast({ title: '已选择规划师', icon: 'success' })
  137. setTimeout(function() {
  138. self.submitting = false
  139. uni.navigateBack()
  140. }, 800)
  141. }
  142. }
  143. }
  144. </script>
  145. <style scoped>
  146. .ts-page {
  147. min-height: 100vh;
  148. background: #F5F7FA;
  149. padding-bottom: 160rpx;
  150. }
  151. .ts-tip {
  152. margin: 24rpx 30rpx;
  153. padding: 20rpx 24rpx;
  154. background: #FFF7E6;
  155. border-radius: 12rpx;
  156. display: flex;
  157. align-items: flex-start;
  158. gap: 12rpx;
  159. }
  160. .ts-tip-icon { font-size: 36rpx; }
  161. .ts-tip-text { font-size: 26rpx; color: #8A6D3B; line-height: 1.6; flex: 1; }
  162. /* 服务类型切换 */
  163. .ts-type-bar {
  164. display: flex;
  165. gap: 20rpx;
  166. margin: 24rpx 30rpx;
  167. }
  168. .ts-type-item {
  169. flex: 1;
  170. background: #fff;
  171. border-radius: 16rpx;
  172. padding: 24rpx 16rpx;
  173. text-align: center;
  174. border: 3rpx solid #E5E7EB;
  175. }
  176. .ts-type-item.active {
  177. border-color: #F97316;
  178. background: #FFFBF0;
  179. }
  180. .ts-type-icon { font-size: 48rpx; display: block; margin-bottom: 8rpx; }
  181. .ts-type-label { font-size: 26rpx; font-weight: 600; color: #333; }
  182. .ts-type-item.active .ts-type-label { color: #F97316; }
  183. /* 服务说明 */
  184. .ts-info-card {
  185. margin: 0 30rpx 24rpx;
  186. background: #fff;
  187. border-radius: 16rpx;
  188. padding: 24rpx;
  189. border-left: 6rpx solid #F97316;
  190. }
  191. .ts-info-title {
  192. font-size: 28rpx;
  193. font-weight: bold;
  194. color: #333;
  195. display: block;
  196. margin-bottom: 12rpx;
  197. }
  198. .ts-info-desc {
  199. font-size: 24rpx;
  200. color: #666;
  201. line-height: 1.8;
  202. display: block;
  203. }
  204. .ts-info-price {
  205. font-size: 24rpx;
  206. color: #F97316;
  207. font-weight: 600;
  208. display: block;
  209. margin-top: 12rpx;
  210. padding-top: 12rpx;
  211. border-top: 1rpx solid #F0F0F0;
  212. }
  213. .ts-info-refund {
  214. font-size: 22rpx;
  215. color: #999;
  216. display: block;
  217. margin-top: 8rpx;
  218. }
  219. /* 规划师列表 */
  220. .ts-list {
  221. margin: 0 30rpx;
  222. display: flex;
  223. flex-direction: column;
  224. gap: 16rpx;
  225. }
  226. .ts-card {
  227. background: #fff;
  228. border-radius: 16rpx;
  229. padding: 24rpx;
  230. border: 3rpx solid #E5E7EB;
  231. transition: all 0.2s;
  232. }
  233. .ts-card.active {
  234. border-color: #F97316;
  235. background: #FFFBF0;
  236. }
  237. .ts-card-top {
  238. display: flex;
  239. align-items: center;
  240. gap: 16rpx;
  241. }
  242. .ts-avatar {
  243. width: 72rpx;
  244. height: 72rpx;
  245. border-radius: 36rpx;
  246. background: #ececec;
  247. border: 2rpx solid #ddd;
  248. color: #888;
  249. font-size: 32rpx;
  250. font-weight: bold;
  251. display: flex;
  252. align-items: center;
  253. justify-content: center;
  254. flex-shrink: 0;
  255. }
  256. .ts-card.active .ts-avatar {
  257. background: #F97316;
  258. border-color: #F97316;
  259. color: #fff;
  260. }
  261. .ts-card-info { flex: 1; }
  262. .ts-card-name {
  263. font-size: 30rpx;
  264. font-weight: 600;
  265. color: #333;
  266. display: block;
  267. }
  268. .ts-card.active .ts-card-name { color: #F97316; }
  269. .ts-card-spec {
  270. font-size: 24rpx;
  271. color: #888;
  272. display: block;
  273. margin-top: 4rpx;
  274. }
  275. .ts-card-tag {
  276. font-size: 22rpx;
  277. color: #4CAF50;
  278. display: block;
  279. margin-top: 4rpx;
  280. }
  281. .ts-card-check {
  282. width: 48rpx;
  283. height: 48rpx;
  284. border-radius: 24rpx;
  285. border: 3rpx solid #d5d5d5;
  286. background: #fff;
  287. color: transparent;
  288. font-size: 26rpx;
  289. line-height: 42rpx;
  290. text-align: center;
  291. flex-shrink: 0;
  292. }
  293. .ts-card.active .ts-card-check {
  294. background: #F97316;
  295. border-color: #F97316;
  296. color: #fff;
  297. }
  298. .ts-card-desc {
  299. margin-top: 12rpx;
  300. font-size: 24rpx;
  301. color: #888;
  302. line-height: 1.6;
  303. }
  304. .ts-card-price {
  305. margin-top: 12rpx;
  306. display: flex;
  307. align-items: baseline;
  308. gap: 4rpx;
  309. }
  310. .ts-price-val {
  311. font-size: 36rpx;
  312. font-weight: bold;
  313. color: #F97316;
  314. }
  315. .ts-price-unit {
  316. font-size: 24rpx;
  317. color: #999;
  318. }
  319. /* 空状态 */
  320. .ts-empty {
  321. display: flex;
  322. flex-direction: column;
  323. align-items: center;
  324. padding: 80rpx 0;
  325. }
  326. .ts-empty-icon { font-size: 80rpx; margin-bottom: 16rpx; }
  327. .ts-empty-text { font-size: 28rpx; color: #bbb; }
  328. .ts-empty-hint { font-size: 24rpx; color: #ddd; margin-top: 8rpx; }
  329. /* 加载中 */
  330. .ts-loading {
  331. display: flex;
  332. justify-content: center;
  333. padding: 60rpx 0;
  334. }
  335. .ts-loading-text { font-size: 26rpx; color: #999; }
  336. /* 底部按钮 */
  337. .ts-bottom-bar {
  338. position: fixed;
  339. bottom: 0;
  340. left: 0;
  341. right: 0;
  342. background: #fff;
  343. padding: 20rpx 30rpx;
  344. padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
  345. box-shadow: 0 -2rpx 12rpx rgba(0,0,0,0.06);
  346. }
  347. .ts-btn-submit {
  348. width: 100%;
  349. height: 88rpx;
  350. line-height: 88rpx;
  351. border-radius: 44rpx;
  352. border: none;
  353. font-size: 30rpx;
  354. text-align: center;
  355. background: linear-gradient(135deg, #F97316, #FB923C);
  356. color: #fff;
  357. }
  358. .ts-btn-submit[disabled] { opacity: 0.4; }
  359. </style>