select.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. <template>
  2. <view class="select-page">
  3. <!-- 顶部状态区 -->
  4. <view class="status-card" v-if="pageState === 'bound'">
  5. <view class="current-label">我的管家</view>
  6. <view class="current-row">
  7. <image class="current-avatar" :src="myButler.avatar || '/static/logo.png'" mode="aspectFill" />
  8. <view class="current-info">
  9. <text class="current-name">{{ myButler.nickname }}</text>
  10. <text class="current-date">{{ formatDate(myButler.assignedAt) }} 绑定</text>
  11. </view>
  12. </view>
  13. <view class="current-desc">{{ myButler.description }}</view>
  14. <button class="btn-change" @click="scrollToList">更换管家</button>
  15. </view>
  16. <view class="upgrade-banner" v-if="pageState === 'locked'" @click="goUpgrade">
  17. <text class="upgrade-text">久久一生(L2)会员专享,升级后可选择专属管家</text>
  18. <text class="upgrade-arrow">›</text>
  19. </view>
  20. <!-- 管家列表 -->
  21. <view class="list-section" id="butlerList">
  22. <view class="section-title">可选管家</view>
  23. <view class="butler-card" v-for="(item, idx) in butlers" :key="idx"
  24. :class="{ active: myButler && myButler.butlerUserId === item.butlerUserId }">
  25. <image class="card-avatar" :src="item.avatar || '/static/logo.png'" mode="aspectFill" />
  26. <view class="card-body">
  27. <view class="card-top">
  28. <text class="card-name">{{ item.nickname }}</text>
  29. <text class="card-slots">剩余名额 {{ item.remainingSlots }}</text>
  30. </view>
  31. <text class="card-desc">{{ item.description || '专业家庭健康管家' }}</text>
  32. </view>
  33. <button class="btn-select"
  34. :disabled="pageState === 'locked' || item.remainingSlots <= 0"
  35. @click="onSelect(item)">
  36. {{ myButler && myButler.butlerUserId === item.butlerUserId ? '当前' : '选择' }}
  37. </button>
  38. </view>
  39. <view class="empty-tip" v-if="butlers.length === 0 && !loading">暂无可选管家</view>
  40. </view>
  41. </view>
  42. </template>
  43. <script>
  44. import { getAvailableButlers, getMyButler, selectButler } from '@/utils/api.js'
  45. export default {
  46. data() {
  47. return {
  48. loading: false,
  49. pageState: 'browse', // browse=可浏览未绑定 / bound=已绑定 / locked=非L2
  50. lockReason: '',
  51. myButler: null,
  52. butlers: [],
  53. keySeq: 0
  54. }
  55. },
  56. onLoad() {
  57. this.initPage()
  58. },
  59. onShow() {
  60. if (this.myButler || this.pageState === 'bound') {
  61. this.refreshMyButler()
  62. }
  63. },
  64. methods: {
  65. formatDate(iso) {
  66. if (!iso) return ''
  67. return iso.substring(0, 19).replace('T', ' ').substring(0, 16)
  68. },
  69. initPage() {
  70. var that = this
  71. that.loading = true
  72. Promise.all([getAvailableButlers(), getMyButler()])
  73. .then(function (resArr) {
  74. var listRes = resArr[0]
  75. var myRes = resArr[1]
  76. if (listRes && listRes.code === 200) {
  77. that.butlers = listRes.data || []
  78. }
  79. if (myRes && myRes.code === 200 && myRes.data) {
  80. that.myButler = myRes.data
  81. that.pageState = 'bound'
  82. }
  83. })
  84. .catch(function () {
  85. uni.showToast({ title: '加载失败', icon: 'none' })
  86. })
  87. .then(function () { that.loading = false })
  88. },
  89. refreshMyButler() {
  90. var that = this
  91. getMyButler().then(function (res) {
  92. if (res && res.code === 200) {
  93. that.myButler = res.data
  94. that.pageState = res.data ? 'bound' : 'browse'
  95. }
  96. })
  97. },
  98. markLocked(msg) {
  99. this.pageState = 'locked'
  100. this.lockReason = msg || ''
  101. },
  102. onSelect(item) {
  103. var that = this
  104. if (that.myButler && that.myButler.butlerUserId === item.butlerUserId) return
  105. uni.showModal({
  106. title: '确认选择',
  107. content: '确定选择「' + item.nickname + '」作为您的专属管家?',
  108. success: function (mr) {
  109. if (!mr.confirm) return
  110. selectButler(item.butlerUserId).then(function (res) {
  111. if (res && res.code === 200) {
  112. uni.showToast({ title: '绑定成功', icon: 'success' })
  113. that.initPage()
  114. } else if (res && res.message && res.message.indexOf('久久一生') !== -1) {
  115. that.markLocked(res.message)
  116. uni.showModal({
  117. title: '会员权益',
  118. content: res.message,
  119. showCancel: false,
  120. success: function () { that.goUpgrade() }
  121. })
  122. } else {
  123. uni.showToast({ title: (res && res.message) || '绑定失败', icon: 'none' })
  124. }
  125. })
  126. }
  127. })
  128. },
  129. scrollToList() {
  130. uni.pageScrollTo({ selector: '#butlerList', duration: 300 })
  131. },
  132. goUpgrade() {
  133. uni.navigateTo({ url: '/pages/membership/upgrade' })
  134. }
  135. }
  136. }
  137. </script>
  138. <style scoped>
  139. .select-page { min-height: 100vh; background: #f5fafe; padding: 24rpx; box-sizing: border-box; display: flex; flex-direction: column; }
  140. .status-card { background: #fff; border-radius: 20rpx; padding: 32rpx; margin-bottom: 24rpx; }
  141. .current-label { font-size: 24rpx; color: #999; margin-bottom: 16rpx; }
  142. .current-row { display: flex; align-items: center; }
  143. .current-avatar { width: 96rpx; height: 96rpx; border-radius: 50%; margin-right: 20rpx; }
  144. .current-info { display: flex; flex-direction: column; }
  145. .current-name { font-size: 34rpx; font-weight: bold; color: #333; }
  146. .current-date { font-size: 22rpx; color: #999; margin-top: 6rpx; }
  147. .current-desc { font-size: 26rpx; color: #666; margin-top: 20rpx; line-height: 1.6; }
  148. .btn-change { margin-top: 24rpx; background: #4a9bd7; color: #fff; font-size: 28rpx; border-radius: 40rpx; }
  149. .upgrade-banner { display: flex; justify-content: space-between; align-items: center; background: #fff7ed; border: 1rpx solid #ffb56b; border-radius: 16rpx; padding: 24rpx; margin-bottom: 24rpx; }
  150. .upgrade-text { font-size: 26rpx; color: #d4770a; flex: 1; }
  151. .upgrade-arrow { font-size: 36rpx; color: #d4770a; margin-left: 12rpx; }
  152. .section-title { font-size: 30rpx; font-weight: bold; color: #333; margin: 8rpx 0 20rpx; }
  153. .butler-card { display: flex; align-items: center; background: #fff; border-radius: 20rpx; padding: 28rpx; margin-bottom: 20rpx; }
  154. .butler-card.active { border: 2rpx solid #4a9bd7; }
  155. .card-avatar { width: 88rpx; height: 88rpx; border-radius: 50%; margin-right: 20rpx; flex-shrink: 0; }
  156. .card-body { flex: 1; min-width: 0; }
  157. .card-top { display: flex; align-items: center; justify-content: space-between; }
  158. .card-name { font-size: 30rpx; font-weight: bold; color: #333; }
  159. .card-slots { font-size: 22rpx; color: #10b981; }
  160. .card-desc { display: block; font-size: 24rpx; color: #888; margin-top: 8rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  161. .btn-select { width: 120rpx; height: 60rpx; line-height: 60rpx; padding: 0; font-size: 26rpx; background: #4a9bd7; color: #fff; border-radius: 30rpx; flex-shrink: 0; margin-left: 16rpx; }
  162. .btn-select[disabled] { background: #ccc; color: #fff; }
  163. .empty-tip { text-align: center; color: #999; font-size: 26rpx; padding: 60rpx 0; }
  164. </style>