soothe-toolbox.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. <template>
  2. <view class="toolbox-container">
  3. <!-- ===== 4-7-8 呼吸练习 ===== -->
  4. <view class="tool-section">
  5. <view class="section-header">
  6. <text class="section-emoji">&#x1F9D8;</text>
  7. <text class="section-title">4-7-8 呼吸练习</text>
  8. </view>
  9. <text class="section-desc">用深呼吸让自己平静下来</text>
  10. <view class="breathing-area" @click="toggleBreathing">
  11. <view :class="['breath-circle', breathPhase]">
  12. <text class="breath-text">{{ breathInstruction }}</text>
  13. </view>
  14. </view>
  15. <text class="breath-hint" v-if="!breathing">点击开始呼吸练习</text>
  16. <text class="breath-count" v-if="breathing">第 {{ breathRound }} 轮</text>
  17. </view>
  18. <!-- ===== 正念练习 ===== -->
  19. <view class="tool-section">
  20. <view class="section-header">
  21. <text class="section-emoji">&#x1F4A1;</text>
  22. <text class="section-title">正念练习</text>
  23. </view>
  24. <view class="mindfulness-cards">
  25. <view
  26. :class="['mind-card', activeMindCard === idx ? 'mind-card-active' : '']"
  27. v-for="(item, idx) in mindfulnessItems"
  28. :key="idx"
  29. @click="activeMindCard = idx">
  30. <text class="mind-card-title">{{ item.title }}</text>
  31. <text class="mind-card-desc">{{ item.desc }}</text>
  32. </view>
  33. </view>
  34. <view class="mindfulness-content" v-if="activeMindCard !== -1">
  35. <text class="mindfulness-text">{{ mindfulnessItems[activeMindCard].content }}</text>
  36. </view>
  37. </view>
  38. <!-- ===== 情绪涂鸦 ===== -->
  39. <view class="tool-section">
  40. <view class="section-header">
  41. <text class="section-emoji">&#x1F3A8;</text>
  42. <text class="section-title">情绪涂鸦</text>
  43. </view>
  44. <text class="section-desc">在纸上自由画下你现在的情绪</text>
  45. <view class="doodle-suggestions">
  46. <view class="doodle-item" v-for="(item, idx) in doodlePrompts" :key="idx">
  47. <text class="doodle-icon">{{ item.icon }}</text>
  48. <text class="doodle-text">{{ item.text }}</text>
  49. </view>
  50. </view>
  51. </view>
  52. <!-- ===== 运动释压 ===== -->
  53. <view class="tool-section">
  54. <view class="section-header">
  55. <text class="section-emoji">&#x1F3CB;</text>
  56. <text class="section-title">运动释压</text>
  57. </view>
  58. <text class="section-desc">简单的伸展动作,释放身体紧张</text>
  59. <view class="exercise-list">
  60. <view class="exercise-item" v-for="(ex, idx) in exercises" :key="idx">
  61. <text class="exercise-name">{{ ex.name }}</text>
  62. <text class="exercise-desc">{{ ex.desc }}</text>
  63. </view>
  64. </view>
  65. </view>
  66. <!-- 底部提示 -->
  67. <view class="disclaimer">
  68. <text class="disclaimer-text">以上内容仅供参考,不能替代专业心理咨询或医疗建议。如有需要,请寻求专业帮助。</text>
  69. </view>
  70. </view>
  71. </template>
  72. <script>
  73. export default {
  74. data() {
  75. return {
  76. breathing: false,
  77. breathPhase: 'inhal',
  78. breathInstruction: '吸气',
  79. breathRound: 0,
  80. breathTimer: null,
  81. activeMindCard: -1,
  82. mindfulnessItems: [
  83. {
  84. title: '5-4-3-2-1 感官练习',
  85. desc: '快速回归当下',
  86. content: '找一个舒服的姿势坐下,深呼吸三次。然后:\n\n看:说出你看到的 5 样东西\n听:说出你听到的 4 种声音\n触:感受你身体的 3 处触觉\n闻:闻到的 2 种气味\n味:品尝到的 1 种味道\n\n这个练习能帮你快速从焦虑中回到当下。'
  87. },
  88. {
  89. title: '身体扫描',
  90. desc: '放松全身',
  91. content: '闭上眼睛,把注意力带到脚趾。感受脚趾的温度和触感。\n\n慢慢向上移动注意力:脚掌 → 脚踝 → 小腿 → 膝盖 → 大腿 → 腹部 → 胸部 → 手指 → 手臂 → 肩膀 → 脖子 → 面部 → 头顶。\n\n在每个部位停留 3-5 秒,感受那里的感觉。'
  92. },
  93. {
  94. title: '感恩三件事',
  95. desc: '培养积极心态',
  96. content: '想一想今天让你感到感恩的三件事。可以是很小的事:\n\n1. 今天吃到了一顿美味的饭\n2. 有人对你说了一句温暖的话\n3. 今天的天气很好\n4. 你学会了一样新东西\n5. 帮助了别人\n\n在心里默默感谢这些美好。'
  97. }
  98. ],
  99. doodlePrompts: [
  100. { icon: '\u{1F308}', text: '画出你今天的情绪彩虹' },
  101. { icon: '\u{1F33F}', text: '画一棵代表你内心的小树' },
  102. { icon: '\u{1F30A}', text: '画出心情波浪' },
  103. { icon: '\u{1F31F}', text: '画出你心中的星星' }
  104. ],
  105. exercises: [
  106. { name: '耸肩放松', desc: '双肩用力向上耸起(靠近耳朵),保持 5 秒,然后突然放松。重复 5 次。' },
  107. { name: '颈部伸展', desc: '慢慢将头向右侧倾斜,右手轻压头部,保持 15 秒。换左侧重复。' },
  108. { name: '猫牛式', desc: '双手双膝着地,吸气时塌腰抬头(牛式),呼气时弓背低头(猫式)。重复 10 次。' },
  109. { name: '蝴蝶式', desc: '坐姿,双脚掌相对,双手握住脚踝,膝盖向两侧打开轻轻上下抖动,像蝴蝶扇动翅膀。' }
  110. ]
  111. }
  112. },
  113. methods: {
  114. toggleBreathing() {
  115. if (this.breathing) {
  116. this.stopBreathing()
  117. } else {
  118. this.startBreathing()
  119. }
  120. },
  121. startBreathing() {
  122. this.breathing = true
  123. this.breathRound = 1
  124. this.doBreathPhase('inhal', 4)
  125. },
  126. doBreathPhase(phase, seconds) {
  127. var self = this
  128. if (!self.breathing) return
  129. self.breathPhase = phase
  130. var instructions = {
  131. inhal: '吸气...',
  132. hold: '屏息...',
  133. exhale: '呼气...'
  134. }
  135. self.breathInstruction = instructions[phase] || ''
  136. self.breathTimer = setTimeout(function() {
  137. if (!self.breathing) return
  138. if (phase === 'inhal') {
  139. self.doBreathPhase('hold', 7)
  140. } else if (phase === 'hold') {
  141. self.doBreathPhase('exhale', 8)
  142. } else {
  143. // exhale done, start next round
  144. var nextRound = self.breathRound + 1
  145. self.breathRound = nextRound
  146. if (nextRound <= 5) {
  147. self.doBreathPhase('inhal', 4)
  148. } else {
  149. self.stopBreathing()
  150. uni.showToast({ title: '完成一组呼吸', icon: 'none' })
  151. }
  152. }
  153. }, seconds * 1000)
  154. },
  155. stopBreathing() {
  156. this.breathing = false
  157. if (this.breathTimer) {
  158. clearTimeout(this.breathTimer)
  159. this.breathTimer = null
  160. }
  161. this.breathPhase = 'inhal'
  162. this.breathInstruction = '吸气'
  163. this.breathRound = 0
  164. }
  165. },
  166. onUnload() {
  167. this.stopBreathing()
  168. }
  169. }
  170. </script>
  171. <style scoped>
  172. .toolbox-container {
  173. min-height: 100vh;
  174. background: linear-gradient(180deg, #F0FDF4 0%, #FFFFFF 50%, #FFF7ED 100%);
  175. padding-bottom: 40rpx;
  176. }
  177. .tool-section {
  178. margin: 24rpx 32rpx;
  179. background: #fff;
  180. border-radius: 20rpx;
  181. padding: 28rpx;
  182. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04);
  183. }
  184. .section-header {
  185. display: flex;
  186. align-items: center;
  187. margin-bottom: 8rpx;
  188. }
  189. .section-emoji {
  190. font-size: 36rpx;
  191. margin-right: 12rpx;
  192. }
  193. .section-title {
  194. font-size: 30rpx;
  195. font-weight: 600;
  196. color: #333;
  197. }
  198. .section-desc {
  199. font-size: 24rpx;
  200. color: #999;
  201. display: block;
  202. margin-bottom: 20rpx;
  203. }
  204. /* ===== 呼吸练习 ===== */
  205. .breathing-area {
  206. display: flex;
  207. align-items: center;
  208. justify-content: center;
  209. padding: 40rpx 0;
  210. }
  211. .breath-circle {
  212. width: 280rpx;
  213. height: 280rpx;
  214. border-radius: 50%;
  215. background: linear-gradient(135deg, #A7F3D0, #6EE7B7);
  216. display: flex;
  217. align-items: center;
  218. justify-content: center;
  219. transition: all 0.3s;
  220. box-shadow: 0 8rpx 40rpx rgba(16, 185, 129, 0.2);
  221. }
  222. .breath-circle.inhal {
  223. animation: breatheIn 4s ease-in-out;
  224. }
  225. .breath-circle.hold {
  226. background: linear-gradient(135deg, #FDE68A, #F59E0B);
  227. box-shadow: 0 8rpx 40rpx rgba(245, 158, 11, 0.3);
  228. }
  229. .breath-circle.exhale {
  230. animation: breatheOut 8s ease-in-out;
  231. }
  232. .breath-text {
  233. font-size: 36rpx;
  234. font-weight: 600;
  235. color: #fff;
  236. }
  237. .breath-hint {
  238. display: block;
  239. text-align: center;
  240. font-size: 24rpx;
  241. color: #999;
  242. }
  243. .breath-count {
  244. display: block;
  245. text-align: center;
  246. font-size: 24rpx;
  247. color: #10B981;
  248. margin-top: 8rpx;
  249. }
  250. /* ===== 正念练习 ===== */
  251. .mindfulness-cards {
  252. display: flex;
  253. flex-direction: column;
  254. gap: 16rpx;
  255. }
  256. .mind-card {
  257. padding: 20rpx;
  258. background: #F9F9F9;
  259. border-radius: 12rpx;
  260. border: 2rpx solid transparent;
  261. }
  262. .mind-card-active {
  263. background: #F0FDF4;
  264. border-color: #10B981;
  265. }
  266. .mind-card-title {
  267. font-size: 26rpx;
  268. font-weight: 600;
  269. color: #333;
  270. display: block;
  271. }
  272. .mind-card-desc {
  273. font-size: 22rpx;
  274. color: #999;
  275. display: block;
  276. margin-top: 4rpx;
  277. }
  278. .mindfulness-content {
  279. margin-top: 16rpx;
  280. padding: 20rpx;
  281. background: #F0FDF4;
  282. border-radius: 12rpx;
  283. border-left: 6rpx solid #10B981;
  284. }
  285. .mindfulness-text {
  286. font-size: 24rpx;
  287. color: #555;
  288. line-height: 1.8;
  289. white-space: pre-wrap;
  290. }
  291. /* ===== 情绪涂鸦 ===== */
  292. .doodle-suggestions {
  293. display: flex;
  294. flex-wrap: wrap;
  295. gap: 16rpx;
  296. }
  297. .doodle-item {
  298. width: calc(50% - 8rpx);
  299. display: flex;
  300. align-items: center;
  301. gap: 12rpx;
  302. padding: 20rpx;
  303. background: #FFFBEB;
  304. border-radius: 12rpx;
  305. box-sizing: border-box;
  306. }
  307. .doodle-icon {
  308. font-size: 36rpx;
  309. }
  310. .doodle-text {
  311. font-size: 24rpx;
  312. color: #666;
  313. flex: 1;
  314. }
  315. /* ===== 运动释压 ===== */
  316. .exercise-list {
  317. display: flex;
  318. flex-direction: column;
  319. gap: 16rpx;
  320. }
  321. .exercise-item {
  322. padding: 20rpx;
  323. background: #F0F9FF;
  324. border-radius: 12rpx;
  325. border-left: 6rpx solid #3B82F6;
  326. }
  327. .exercise-name {
  328. font-size: 26rpx;
  329. font-weight: 600;
  330. color: #2563EB;
  331. display: block;
  332. margin-bottom: 6rpx;
  333. }
  334. .exercise-desc {
  335. font-size: 24rpx;
  336. color: #666;
  337. line-height: 1.6;
  338. display: block;
  339. }
  340. /* ===== 免责声明 ===== */
  341. .disclaimer {
  342. margin: 32rpx;
  343. padding: 20rpx;
  344. background: #FEF3C7;
  345. border-radius: 12rpx;
  346. }
  347. .disclaimer-text {
  348. font-size: 22rpx;
  349. color: #92400E;
  350. line-height: 1.6;
  351. }
  352. </style>