member-action-detail.vue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. <template>
  2. <view class="detail-container">
  3. <!-- 顶部导航 -->
  4. <!-- 品牌定位语(登录前显示) -->
  5. <view class="brand-strap" v-if="!isLoggedIn">
  6. <text class="brand-strap-text">改善全家关系与沟通能力</text>
  7. </view>
  8. <!-- 游客引导语 -->
  9. <view class="monthly-guidance" v-if="!isLoggedIn">
  10. <text class="guidance-icon">🌿</text>
  11. <text class="guidance-text">{{ monthlyGuidance }}</text>
  12. </view>
  13. <!-- 登录后内容 -->
  14. <template v-else>
  15. <!-- 五维能量条 -->
  16. <FamilyEnergyBar
  17. dimensionCode="action"
  18. :sandboxData="sandboxData"
  19. :dualDimension="dualDimension" />
  20. <!-- 成员信息卡 -->
  21. <view class="member-card" v-if="memberInfo">
  22. <view class="member-avatar action-avatar">
  23. <text class="avatar-text">{{ memberInfo.name && memberInfo.name.charAt(0) || '孩' }}</text>
  24. </view>
  25. <view class="member-info">
  26. <text class="member-name">{{ memberInfo.name || '孩子' }}</text>
  27. <text class="member-role">行动维度</text>
  28. </view>
  29. <view class="member-score" v-if="dualDimension">
  30. <text class="score-value action-score">{{ dualDimension.energy || 0 }}</text>
  31. <text class="score-label">能量值</text>
  32. </view>
  33. </view>
  34. <!-- 家庭成员关系图谱(只读) -->
  35. <FamilyRelationGraph
  36. dimensionCode="action"
  37. :selfId="selfId"
  38. :members="graphMembers"
  39. :energyMap="energyMapForGraph"
  40. :intimacyMap="intimacyMapForGraph"
  41. :interactive="false" />
  42. <!-- 今日任务 -->
  43. <DimensionTasks
  44. :memberId="memberId"
  45. @taskClick="onTaskClick"
  46. @moreTasks="goTasks" />
  47. <!-- 活动 -->
  48. <DimensionActivities
  49. :isLoggedIn="true"
  50. @activityClick="goActivityDetail"
  51. @moreActivities="goMoreActivities" />
  52. <!-- 商品 -->
  53. <DimensionProducts
  54. :isLoggedIn="true"
  55. @productClick="goProductDetail"
  56. @moreProducts="goMoreProducts" />
  57. <!-- 行维度介绍卡片 -->
  58. <DimensionIntroCard dimCode="action" v-if="isLoggedIn" learn-more-url="/pages/action-detail/dimension-intro" />
  59. </template>
  60. <!-- 底部占位 -->
  61. <view class="bottom-spacer"></view>
  62. </view>
  63. </template>
  64. <script>
  65. import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
  66. import FamilyRelationGraph from '../../components/FamilyRelationGraph.vue'
  67. import DimensionTasks from '../../components/DimensionTasks.vue'
  68. import DimensionActivities from '../../components/DimensionActivities.vue'
  69. import DimensionProducts from '../../components/DimensionProducts.vue'
  70. import DimensionIntroCard from '../../components/DimensionIntroCard.vue'
  71. import { getEnergyOverview, getFamilyEnergySandbox, getChildren } from '../../utils/api.js'
  72. export default {
  73. components: { FamilyEnergyBar, FamilyRelationGraph, DimensionTasks, DimensionActivities, DimensionProducts, DimensionIntroCard },
  74. data() {
  75. return {
  76. memberId: null,
  77. selfId: null,
  78. memberInfo: null,
  79. sandboxData: null,
  80. dualDimension: null,
  81. isLoggedIn: false
  82. }
  83. },
  84. onLoad: function(options) {
  85. this.memberId = options.memberId || uni.getStorageSync('currentChildId') || null
  86. },
  87. onShow: function() {
  88. var token = uni.getStorageSync('token')
  89. this.isLoggedIn = !!token
  90. if (!this.isLoggedIn) return
  91. this.selfId = uni.getStorageSync('userId') || null
  92. this.loadChildren()
  93. this.loadSandboxData()
  94. if (this.memberId) {
  95. this.loadEnergyData()
  96. }
  97. },
  98. computed: {
  99. graphMembers: function() {
  100. if (!this.sandboxData || !this.sandboxData.members) return []
  101. return this.sandboxData.members.map(function(m) {
  102. return {
  103. id: m.memberId || m.id,
  104. nickname: m.name || m.nickname || '成员',
  105. memberType: m.memberType || 'child'
  106. }
  107. })
  108. },
  109. energyMapForGraph: function() {
  110. var map = {}
  111. if (this.sandboxData && this.sandboxData.members) {
  112. for (var i = 0; i < this.sandboxData.members.length; i++) {
  113. var m = this.sandboxData.members[i]
  114. map[m.memberId || m.id] = {
  115. bodyScore: m.bodyScore || 0,
  116. mindScore: m.mindScore || 0,
  117. actionScore: m.actionScore || 0
  118. }
  119. }
  120. }
  121. return map
  122. },
  123. intimacyMapForGraph: function() {
  124. return {}
  125. },
  126. /** 每月引导语 */
  127. monthlyGuidance: function() {
  128. var month = new Date().getMonth()
  129. var messages = [
  130. '一月:新年伊始,增进家庭沟通',
  131. '二月:春节团聚,珍惜亲子时光',
  132. '三月:春暖花开,多陪伴孩子',
  133. '四月:清明踏青,共享家庭活动',
  134. '五月:劳动教育,培养家庭责任感',
  135. '六月:儿童节,倾听孩子心声',
  136. '七月:暑期陪伴,增进亲子关系',
  137. '八月:开学准备,共同规划',
  138. '九月:新学期,建立家庭规则',
  139. '十月:国庆假期,家庭协作时光',
  140. '十一月:感恩季节,表达家庭关爱',
  141. '十二月:年终总结,回顾家庭成长'
  142. ]
  143. return messages[month] || messages[0]
  144. }
  145. },
  146. methods: {
  147. loadChildren: function() {
  148. var self = this
  149. getChildren().then(function(res) {
  150. if (res.code === 200 && res.data) {
  151. for (var i = 0; i < res.data.length; i++) {
  152. if (res.data[i].memberId === self.memberId) {
  153. self.memberInfo = res.data[i]
  154. break
  155. }
  156. }
  157. if (!self.memberInfo && res.data.length > 0) {
  158. self.memberInfo = res.data[0]
  159. self.memberId = res.data[0].memberId
  160. }
  161. }
  162. }).catch(function() {})
  163. },
  164. loadSandboxData: function() {
  165. var self = this
  166. getFamilyEnergySandbox().then(function(res) {
  167. if (res && res.data) self.sandboxData = res.data
  168. }).catch(function() {})
  169. },
  170. loadEnergyData: function() {
  171. var self = this
  172. getEnergyOverview(this.memberId).then(function(res) {
  173. if (res && res.data) {
  174. var dims = res.data.dimensions
  175. if (dims && dims.length > 0) {
  176. for (var i = 0; i < dims.length; i++) {
  177. if (dims[i].code === 'action') {
  178. self.dualDimension = dims[i]
  179. break
  180. }
  181. }
  182. }
  183. }
  184. }).catch(function() {})
  185. },
  186. onTaskClick: function(task) {
  187. uni.navigateTo({ url: '/pages/tasks/tasks' })
  188. },
  189. goActivityDetail: function(act) {
  190. if (act && act.id) uni.navigateTo({ url: '/pages/activity/activity-detail/activity-detail?id=' + act.id })
  191. },
  192. goMoreActivities: function() {
  193. uni.navigateTo({ url: '/pages/activity/index' })
  194. },
  195. goProductDetail: function(prod) {
  196. if (prod && prod.id) uni.navigateTo({ url: '/pages/discover-detail/product-detail/product-detail?id=' + prod.id })
  197. },
  198. goMoreProducts: function() {
  199. uni.navigateTo({ url: '/pages/shop/index/index' })
  200. },
  201. goTasks: function() { uni.navigateTo({ url: '/pages/tasks/tasks' }) },
  202. goBack: function() { uni.navigateBack() }
  203. }
  204. }
  205. </script>
  206. <style scoped>
  207. .detail-container {
  208. min-height: 100vh;
  209. background: #f5f7fa;
  210. padding-bottom: 40rpx;
  211. }
  212. .nav-back-icon {
  213. font-size: 36rpx;
  214. color: #333;
  215. }
  216. .member-card {
  217. display: flex;
  218. flex-direction: row;
  219. align-items: center;
  220. margin: 20rpx 30rpx;
  221. background: #fff;
  222. border-radius: 20rpx;
  223. padding: 24rpx;
  224. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  225. }
  226. .member-avatar {
  227. width: 80rpx;
  228. height: 80rpx;
  229. border-radius: 40rpx;
  230. background: linear-gradient(135deg, #4CAF50, #81C784);
  231. display: flex;
  232. align-items: center;
  233. justify-content: center;
  234. margin-right: 20rpx;
  235. }
  236. .action-avatar {
  237. background: linear-gradient(135deg, #10B981, #34D399);
  238. }
  239. .avatar-text {
  240. font-size: 32rpx;
  241. color: #fff;
  242. font-weight: bold;
  243. }
  244. .member-info {
  245. flex: 1;
  246. display: flex;
  247. flex-direction: column;
  248. }
  249. .member-name {
  250. font-size: 30rpx;
  251. font-weight: bold;
  252. color: #333;
  253. }
  254. .member-role {
  255. font-size: 24rpx;
  256. color: #999;
  257. margin-top: 4rpx;
  258. }
  259. .member-score {
  260. display: flex;
  261. flex-direction: column;
  262. align-items: center;
  263. }
  264. .score-value {
  265. font-size: 40rpx;
  266. font-weight: bold;
  267. color: #4CAF50;
  268. }
  269. .action-score {
  270. color: #10B981;
  271. }
  272. .score-label {
  273. font-size: 22rpx;
  274. color: #999;
  275. }
  276. .bottom-spacer {
  277. height: 40rpx;
  278. }
  279. /* 品牌定位语 */
  280. .brand-strap {
  281. margin: 16rpx 30rpx 0;
  282. padding: 20rpx 28rpx;
  283. background: linear-gradient(135deg, #F0FFF4, rgba(16,185,129,0.12));
  284. border-radius: 16rpx;
  285. border-left: 4rpx solid #10B981;
  286. }
  287. .brand-strap-text {
  288. font-size: 26rpx;
  289. color: #065F46;
  290. font-weight: 500;
  291. }
  292. /* 每月引导语 */
  293. .monthly-guidance {
  294. margin: 16rpx 30rpx 0;
  295. padding: 20rpx 28rpx;
  296. background: linear-gradient(135deg, #F0FFF4, rgba(16,185,129,0.08));
  297. border-radius: 16rpx;
  298. border-left: 4rpx solid #10B981;
  299. display: flex;
  300. align-items: center;
  301. gap: 12rpx;
  302. }
  303. .guidance-icon { font-size: 32rpx; }
  304. .guidance-text {
  305. font-size: 26rpx;
  306. color: #065F46;
  307. font-weight: 500;
  308. }
  309. </style>