5230383 4 maanden geleden
bovenliggende
commit
420b1e8a6d
96 gewijzigde bestanden met toevoegingen van 1894 en 50 verwijderingen
  1. 286 0
      README.md
  2. 2 1
      angular.json
  3. BIN
      assets/img/chuangzuo.webp
  4. BIN
      assets/img/douban.png
  5. BIN
      assets/img/sjym.jpg
  6. BIN
      assets/img/sqym.jpg
  7. BIN
      assets/img/tjym.jpg
  8. BIN
      assets/img/wenku.webp
  9. BIN
      assets/img/xcz.png
  10. BIN
      assets/img/xiangqing.webp
  11. BIN
      douban.png
  12. BIN
      imag/sqym.jpg
  13. 141 3
      package-lock.json
  14. 3 0
      package.json
  15. BIN
      sjym.jpg
  16. BIN
      sqym.jpg
  17. 27 2
      src/app/app-routing.module.ts
  18. 6 0
      src/app/app.component.ts
  19. 103 12
      src/app/tab1/tab1.page.html
  20. 29 0
      src/app/tab1/tab1.page.scss
  21. 37 3
      src/app/tab1/tab1.page.ts
  22. 6 1
      src/app/tab2/tab2-routing.module.ts
  23. 94 10
      src/app/tab2/tab2.page.html
  24. 20 1
      src/app/tab2/tab2.page.ts
  25. 85 8
      src/app/tab3/tab3.page.html
  26. 17 2
      src/app/tab3/tab3.page.ts
  27. 3 0
      src/app/tabs/tabs-routing.module.ts
  28. 8 6
      src/app/tabs/tabs.page.html
  29. BIN
      src/assets/image/111.jpg
  30. BIN
      src/assets/image/bj1.jpg
  31. BIN
      src/assets/image/dt1.jpg
  32. BIN
      src/assets/image/dt2.jpg
  33. BIN
      src/assets/image/dt3.jpg
  34. BIN
      src/assets/image/dt4.jpg
  35. BIN
      src/assets/image/dt5.jpg
  36. BIN
      src/assets/image/dt6.jpg
  37. BIN
      src/assets/image/global.jpg
  38. BIN
      src/assets/image/lb2.jpg
  39. BIN
      src/assets/image/lb3.jpg
  40. BIN
      src/assets/image/movie.jpg
  41. BIN
      src/assets/image/mzt.jpg
  42. BIN
      src/assets/image/top.jpg
  43. BIN
      src/assets/image/tx1.jpg
  44. BIN
      src/assets/image/tx2.jpg
  45. BIN
      src/assets/image/upcoming.jpg
  46. BIN
      src/assets/image/wdl.jpg
  47. BIN
      src/assets/image/zd1.jpg
  48. BIN
      src/assets/image/zd2.jpg
  49. BIN
      src/assets/image/zhudi.jpg
  50. 12 0
      src/assets/swiper/swiper.min.css
  51. 2 1
      src/index.html
  52. 17 0
      src/modules/album/album-routing.module.ts
  53. 20 0
      src/modules/album/album.module.ts
  54. 17 0
      src/modules/album/album.page.html
  55. 0 0
      src/modules/album/album.page.scss
  56. 17 0
      src/modules/album/album.page.spec.ts
  57. 22 0
      src/modules/album/album.page.ts
  58. 36 0
      src/modules/album/album.service.ts
  59. 17 0
      src/modules/dongt/dongt-routing.module.ts
  60. 20 0
      src/modules/dongt/dongt.module.ts
  61. 74 0
      src/modules/dongt/dongt.page.html
  62. 0 0
      src/modules/dongt/dongt.page.scss
  63. 17 0
      src/modules/dongt/dongt.page.spec.ts
  64. 26 0
      src/modules/dongt/dongt.page.ts
  65. 17 0
      src/modules/favorites/favorites-routing.module.ts
  66. 20 0
      src/modules/favorites/favorites.module.ts
  67. 23 0
      src/modules/favorites/favorites.page.html
  68. 0 0
      src/modules/favorites/favorites.page.scss
  69. 17 0
      src/modules/favorites/favorites.page.spec.ts
  70. 28 0
      src/modules/favorites/favorites.page.ts
  71. 58 0
      src/modules/favorites/favorites.service.ts
  72. 18 0
      src/modules/user/auth.guard.spec.ts
  73. 10 0
      src/modules/user/auth.guard.ts
  74. 17 0
      src/modules/user/edit-info/edit-info-routing.module.ts
  75. 20 0
      src/modules/user/edit-info/edit-info.module.ts
  76. 47 0
      src/modules/user/edit-info/edit-info.page.html
  77. 0 0
      src/modules/user/edit-info/edit-info.page.scss
  78. 17 0
      src/modules/user/edit-info/edit-info.page.spect.ts
  79. 58 0
      src/modules/user/edit-info/edit-info.page.ts
  80. 17 0
      src/modules/user/login/login-routing.module.ts
  81. 20 0
      src/modules/user/login/login.module.ts
  82. 33 0
      src/modules/user/login/login.page.html
  83. 0 0
      src/modules/user/login/login.page.scss
  84. 17 0
      src/modules/user/login/login.page.spec.ts
  85. 93 0
      src/modules/user/login/login.page.ts
  86. 17 0
      src/modules/user/mine/mine-routing.module.ts
  87. 20 0
      src/modules/user/mine/mine.module.ts
  88. 24 0
      src/modules/user/mine/mine.page.html
  89. 0 0
      src/modules/user/mine/mine.page.scss
  90. 17 0
      src/modules/user/mine/mine.page.spec.ts
  91. 28 0
      src/modules/user/mine/mine.page.ts
  92. 14 0
      src/modules/user/user-routing.module.ts
  93. 14 0
      src/modules/user/user.module.ts
  94. 65 0
      src/theme/variables.scss
  95. BIN
      tjym.jpg
  96. 1 0
      tsconfig.json

+ 286 - 0
README.md

@@ -0,0 +1,286 @@
+
+# 推荐类App产品策划书
+
+# 产品介绍
+> 在繁忙的日常生活中,你是否渴望找到一款能为你量身打造娱乐生活的神器?悦享 Joyful app,正是你寻找的贴心小助手。无论你想看电影、读书、品尝美食还是尝试新运动,它都能为你提供精准、个性化的推荐。悦享Joyful app致力于为你带来更加丰富多彩的娱乐生活。让我们一起开启这段奇妙的推荐之旅吧!在这里,你将发现更多未知的精彩和乐趣。快来下载乐享定制推荐app,让生活因推荐而更美好!
+
+- 产品名称:悦享 Joyful
+- 产品代码:Joyful
+- 产品Logan
+    - 在繁星点点的娱乐中,找到你的那一颗。
+- 基础功能
+    - 根据用户心情为用户定制最适合的娱乐建议。
+    - 社交分享,与朋友分享你的喜好和发现
+- 产品简介(200字)
+    - 悦享 Joyful(产品代码:Joyful)是一款智能推荐的创新产品。我们的产品理念是“在繁星点点的娱乐中,找到你的那一颗”。想象一下,你的个人娱乐顾问就在你手中,随时为你提供定制化的推荐。我们为你打造了这款全新的娱乐推荐APP——“悦享 Joyful”。在“悦享 Joyful”里,你将发现无限可能,尽情沉浸在电影、书籍、美食、运动、游戏、音乐的海洋中。我们以用户为核心,深入了解你的喜好。无论你是电影发烧友、书籍狂热者,还是美食猎人、音乐达人......我们都能为你推荐最适合你的内容。通过分析你的历史记录、浏览习惯以及评价反馈,我们为你量身打造独一无二的娱乐体验。并且我们的智能推荐系统不仅能为你推荐你可能喜欢的内容,还能根据你的心情、时间和地点为你定制最合适的娱乐建议。无论是想在闲暇时光阅读一本好书,还是在工作间隙听一首轻松的音乐,“悦享 Joyful”都能满足你的需求。
+
+
+
+
+# 背景分析
+## 政策背景
+> 随着数字化时代的来临,信息爆炸已成为不可忽视的现实。在这一背景下,个性化服务成为了各行业追求用户体验和市场竞争力的关键。政策层面也在积极鼓励和支持科技创新,特别是在满足人民日益增长的美好生活需求方面。
+
+1. **数据隐私与保护**:随着大数据和人工智能技术的广泛应用,用户数据的隐私和安全成为了重要议题。政府近年来加强了对数据保护法规的制定和实施,为app运营者提供了明确的法律指引,保障用户数据在合法、合规的前提下得到充分利用。
+
+2. **个性化推荐服务的政策支持**:政府积极推动数字化转型,特别是在提升公共服务和消费者体验方面。个性化推荐技术作为实现这一目标的重要手段,得到了政策的支持和鼓励。例如,通过设立创新基金、税收优惠等方式,鼓励企业研发和应用先进的推荐算法,为用户提供更加精准的服务。
+
+3. **促进文化消费和体育健康**:这款app的设计不仅涵盖了娱乐内容,还涉及到了文化消费和体育健康等领域。政府在这些领域也有相应的政策支持,如推动文化产业的发展、鼓励体育健身等。通过这款app,用户可以更加方便地获取到优质的文化和体育资源,进一步促进相关产业的发展。
+
+4. **促进数字经济与实体经济的融合发展**:随着电商和互联网的不断发展,数字经济和实体经济之间的联系日益紧密。这款app的设计能够推动线上线下融合,将线上的推荐服务与线下的实体消费相结合,为实体经济带来新的增长点。政府也鼓励企业探索新的商业模式和服务模式,促进数字经济和实体经济的协同发展。
+
+
+## 行业背景
+> 在数字化、网络化的现代社会,人们的生活节奏日益加快,娱乐方式也变得多样化。随着智能设备的普及,人们越来越依赖手机等电子设备来获取信息和满足娱乐需求。在这样的背景下,一个能够个性化推荐电影、书籍、美食、音乐等娱乐内容的app具有巨大的市场潜力和用户价值。
+
+行业背景方面,首先,随着技术的不断进步,尤其是大数据和人工智能的发展,个性化推荐算法得到了显著提升,能够更准确地分析用户的喜好和偏好,为用户提供更精准的推荐。这为设计一款可以根据人们喜好推荐娱乐内容的app提供了强有力的技术支持。
+
+其次,从市场需求来看,人们对于个性化、定制化的服务需求日益增长。在电影、书籍、美食、音乐等领域,由于种类繁多、选择广泛,人们往往难以快速找到自己感兴趣的内容。因此,一个能够智能推荐、节省用户时间和精力的app将受到广大用户的欢迎。
+
+此外,娱乐行业本身也在不断发展和变化。电影、书籍、美食、音乐等领域不断涌现出新的作品和风格,这为app提供了丰富的推荐资源。同时,随着社交媒体的兴起,人们的娱乐需求和审美观念也在发生变化,这也要求app能够紧跟时代潮流,为用户提供最新、最热门的娱乐内容。
+
+综上所述,设计一款可以按照人们喜好推荐电影书籍美食音乐等娱乐的app具有广阔的市场前景和应用价值。它不仅可以满足用户的个性化需求,还可以推动娱乐行业的发展和创新。
+
+
+
+## 社会背景
+> 在快节奏的现代社会中,科技的飞速发展不仅改变了人们的生活方式,更深刻地影响着人们的娱乐选择。随着智能手机和移动互联网的普及,人们越来越倾向于通过移动设备来满足自己的娱乐需求。然而,面对海量的电影、书籍、美食和音乐资源,如何从中挑选出符合自己喜好的内容,成为了一个不小的挑战。
+
+在这样的背景下,个性化推荐系统应运而生。这类系统利用先进的算法,通过分析用户的喜好和行为模式,为他们提供量身定制的娱乐推荐。然而,尽管市面上已经存在不少类似的推荐系统,但往往因为缺乏深入的用户洞察和个性化的服务,难以满足用户的多样化需求。
+
+因此,一款能够真正按照人们喜好,为人们推荐电影、书籍、美食和音乐的APP,显得尤为珍贵。这款APP不仅需要具备强大的算法支持,还需要深入了解用户的内心世界,理解他们的喜好和品味。通过不断学习和优化,这款APP能够逐渐与用户建立起深厚的情感联系,成为他们生活中不可或缺的一部分。
+
+同时,这款APP的出现也反映了社会对于个性化需求的日益重视。在追求多元化的今天,人们不再满足于一成不变的娱乐内容,而是希望根据自己的喜好和需求,获得更加丰富、多元的体验。这款APP的成功,将推动整个社会向着更加个性化、多样化的方向发展。
+
+# 需求分析
+## 用户分析(围绕不同身份的用户故事展开)
+> 悦享 Joyful 多维度探索用户的娱乐喜好与个性化推荐创作产品,潜在的用户群体可以包括但不限于以下几类具有代表性的群体:
+
+1. **职场白领:繁忙中的小确幸**
+职场白领渴望在短暂的休息时间里找到放松方式。通过APP设定喜好,系统推荐适合口味的电影和书籍。同时,推荐附近美食店或健身课程。
+
+2. **时尚达人:品味生活的每一个细节**
+时尚达人追求潮流,看重品味和个性。通过APP可以浏览最新时尚资讯,了解服装潮流和搭配技巧。推荐适合衣服、鞋子和配饰。可以在APP上建立自己的虚拟衣橱,展示自己的穿搭成果,与其他时尚爱好者交流心得。
+
+3. **家庭主妇:温馨生活中的点滴陪伴**
+家庭主妇热爱生活。她们喜欢阅读关于美食、养生的书籍,或者是观看家庭类电视节目。通过APP,她们可以找到适合自己的书籍和电影,也可以在APP上学习新菜谱和烹饪技巧。推荐适合家庭参与的运动项目,增进感情。
+
+4. **大学生:探索世界的无限可能**
+大学生充满好奇和探索。喜欢阅读各种书籍,观看各种电影,也热衷于尝试各种新鲜的事物。通过APP,他们可以根据兴趣和喜好,浏览海量资源。APP还可以根据学习需求,推荐相关的学术资源和讲座信息。
+
+5. **旅行者:发现旅途中的美好与惊喜**
+旅行者热爱探索世界各地的风土人情。他们可以在APP上查看目的地的美食地图、运动活动信息以及购物指南等。APP还可以根据他们的旅行路线和喜好,为他们推荐适合的电影和书籍,让他们在旅途中享受精神上的满足和愉悦。
+
+6. **艺术爱好者:品味艺术与生活的交融**
+艺术爱好者热衷于欣赏各种艺术作品和参加艺术活动。这款APP可以为他们提供一个全方位的艺术欣赏平台。他们可以在APP上浏览最新的艺术作品和展览信息,观看相关的电影和纪录片,也可以在APP上购买自己喜欢的艺术品和书籍。同时,APP还可以根据他们的艺术喜好和品味,为他们推荐适合的电影、书籍和音乐等娱乐资源,让他们在享受艺术的同时得到心灵的满足和愉悦。
+
+通过针对这些具有代表性的用户群体的需求和特点,可以更好地定位产品的市场定位和推广策略,吸引更多潜在用户体验和使用悦享 Joyful产品。
+
+## 痛点分析
+> 在设计这款能够根据人们喜好推荐电影、书籍、美食、音乐的app时,我们首先需要深入了解用户在使用类似产品时所遇到的痛点。以下是一些可能的痛点分析,可以帮助我们更有针对性地满足用户需求:
+
+1. **个性化推荐不足**:许多现有的推荐系统往往基于简单的算法或流行的趋势进行推荐,而忽视了用户独特的品味和偏好。这导致用户收到的推荐内容往往与他们真正感兴趣的相差甚远。
+
+2. **信息过载**:在众多的电影、书籍、美食、音乐等选择中,用户很容易感到迷茫和疲惫。他们希望能够快速找到那些真正值得尝试的内容,而不是在海量信息中漫无目的地浏览。
+
+3. **缺乏互动性**:大多数推荐系统都是单向的,即系统向用户推荐内容,但用户无法直接对推荐结果进行反馈或调整。这导致推荐效果往往不够精准,也无法根据用户的反馈进行改进。
+
+4. **跨领域推荐受限**:很多用户喜欢多种类型的娱乐内容,如既喜欢看书也喜欢听音乐。然而,现有的推荐系统往往只能在一个领域内进行推荐,无法有效地跨领域整合和推荐。
+
+5. **推荐内容质量参差不齐**:由于数据来源的多样性,推荐内容的质量往往难以保证。有些推荐可能基于低质量的数据或误导性的信息,从而影响到用户的体验和满意度。
+
+6. **隐私和安全问题**:在收集用户数据以进行个性化推荐时,用户的隐私和安全可能面临风险。用户需要确保他们的数据被妥善保管,并且不会被滥用或泄露。
+
+通过深度分析不同用户群体的痛点需求、共性需求和差异化需求,可以更有针对性地设计产品功能和服务,满足用户的多样化需求,提升产品的用户体验和市场竞争力。
+
+## 功能分析
+
+一. **用户画像与个性化推荐**
+
+1. **深度用户画像**:首先,需要设计一个详尽的用户画像系统,通过用户注册时的基本信息、使用行为、评价反馈等多维度数据,描绘出每位用户的喜好、兴趣和需求。
+
+2. **个性化推荐算法**:基于用户画像,构建一套智能的个性化推荐算法。这套算法应能够实时更新,根据用户的使用习惯变化,动态调整推荐内容。
+3. **推荐理由展示**:在推荐每项内容时,附上简短的推荐理由,让用户了解为何该内容会出现在他们的推荐列表中。
+
+二. **多维度内容推荐**
+1. **电影推荐**:根据用户的观影历史、评分和喜好类型,推荐适合的电影。可以设置多个子分类,如新片推荐、经典重温、获奖佳作等。
+2. **书籍推荐**:结合用户的阅读习惯和兴趣,为用户推荐各类书籍。还可以增加书籍简介、作者介绍、书评等辅助功能,帮助用户快速了解书籍内容。
+3. **美食推荐**:基于用户的地理位置和口味偏好,推荐附近的美食餐厅和特色菜品。还可以增设用户上传美食照片和评价的功能,增加互动性和用户黏性。
+4. **音乐推荐**:根据用户的听歌历史和音乐风格偏好,推荐适合的音乐。可以设置多种推荐场景,如工作、运动、旅行等,为用户提供更加贴心的音乐服务。
+
+
+三. **互动与交流**
+
+1. **用户社区**:设置一个用户社区,让用户可以在其中分享自己的观影、阅读、美食和音乐体验,与其他用户交流心得和感受。
+评分与点评:允许用户对推荐的内容进行评分和点评,这些反馈将成为推荐算法的重要依据,使推荐内容更加精准。
+
+2. **个性化标签**:鼓励用户为自己添加标签,如“电影控”、“美食家”等,帮助其他用户更好地了解他们的兴趣和喜好。
+
+四.**个性化设置与优化**
+
+1. **内容筛选**:允许用户根据自己的需求,设置推荐内容的筛选条件,如价格、评分、类型等。
+2. **通知与提醒**:为用户提供个性化的通知和提醒服务,如新片上映、特价优惠等,让用户不会错过任何精彩内容。
+3. **反馈与优化**:设置用户反馈渠道,鼓励用户提出意见和建议,不断优化app的功能和用户体验。
+
+
+# 竞品分析
+
+
+
+# 确认核心功能
+
+> 参考文档:https://www.woshipm.com/evaluating/4746511.html
+> 参考文档:https://www.woshipm.com/evaluating/3336147.htmll
+
+## 竞品的搜索和整理
+> 个人娱乐顾问就在你手中,随时为你提供定制化的推荐
+- 参考娱乐推荐的类别和参数选项
+   - 豆瓣 https://www.douban.com/doulist/936168/
+   - 微博 https://weibo.com/newlogin?tabtype=weibo&gid=102803&openLoginLayer=0&url=https%3A%2F%2Fweibo.com%2F
+- 参考界面UI设计
+     - 豆瓣App https://www.douban.com/doulist/936168/
+   - 微博 https://weibo.com/newlogin?tabtype=weibo&gid=102803&openLoginLayer=0&url=https%3A%2F%2Fweibo.com%2F
+- 参考故事数据级
+   - https://github.com/javayhu/poetry
+
+## 竞品功能拆解各维度对比
+
+- 与我们最相关的核心功能:个性推荐、推荐评分
+   - 用户操作逻辑
+   - UI界面布局
+   - 产品结构及页面结构的分析
+    ![](./assets/img/douban.png)
+- 分析他们的用户主体和盈利模式
+
+
+# 核心功能的产品开发计划
+## 确认核心功能
+- 首页
+- 分类
+- 社区
+- 我的
+
+## 产品结构图梳理
+> 参考:https://www.woshipm.com/pd/5405037.html
+
+### 创作页面的产品结构
+
+1. **推荐页面**
+    - 个性化推荐
+        - 根据用户喜好和历史记录推荐内容
+        - 心情推荐:根据用户选择的心情推荐相关内容
+    - 热门推荐
+        - 热门电影、书籍、美食、运动、游戏、音乐推荐
+    - 最新更新
+        - 最新上架的电影、书籍、美食、运动、游戏、音乐推荐
+
+2. **推荐内容模块**
+    - 每个推荐内容包含:
+        - 标题
+        - 封面图片
+        - 简介
+        - 评分/喜爱指数
+        - 点击进入详细内容页
+
+3.  **推荐内容详细页**
+    - 详细内容展示
+        - 内容标题
+        - 封面图片
+        - 详细描述
+        - 相关推荐内容
+    - 用户交互
+        - 点赞
+        - 收藏
+        - 评论
+        - 分享
+
+4.  **心情推荐模块**
+    - 用户选择心情/情绪
+        - 快乐、悲伤、兴奋、放松等
+    - 根据心情推荐相关内容
+        - 适合当前心情的电影、书籍、音乐等推荐
+
+5.  **热门推荐模块**
+    - 根据热门度排列的推荐内容列表
+        - 可能包括热门电影、畅销书籍、流行美食等
+
+6. **最新更新模块**
+    - 展示最新上架的内容
+        - 最新电影、书籍、美食、运动活动等推荐
+
+7. **用户交互**
+    - 点击推荐内容进入详细页
+    - 点赞、收藏、评论和分享推荐内容
+    - 在心情推荐模块选择心情查看相关推荐内容
+
+
+
+
+
+## 信息结构图梳理
+> 参考:https://www.woshipm.com/pd/5405037.html
+
+
+- 首页
+    - 欢迎页面
+    - 用户个性化推荐
+    - 热门推荐内容
+    - 心情推荐模块
+    - 最新更新内容
+
+- 分类
+    - 电影推荐
+    - 书籍推荐
+    - 美食推荐
+    - 运动推荐
+    - 游戏推荐
+    - 音乐推荐
+    - 每个分类包含:
+        - 推荐内容列表
+        - 热门排行榜
+        - 最新更新
+
+- 社区
+    - 用户分享内容
+    - 用户评论与点赞
+    - 精彩活动推荐
+    - 热门话题讨论区
+
+- 我的
+    - 个人信息设置
+    - 我的收藏
+    - 我的足迹
+    - 我的推荐历史
+    - 我的社区互动记录
+
+- 其他页面
+    - 搜索功能
+    - 消息通知
+    - 设置与帮助
+
+- 用户交互
+    - 点击分类进入对应推荐内容页面
+    - 点击社区查看用户分享内容和评论
+    - 点击我的查看个人信息和互动记录
+    - 点击搜索进行内容查找
+    - 接收消息通知并进行相关操作
+    - 设置页面可进行账号设置和应用帮助
+
+- 用户体验
+    - 个性化推荐让用户快速找到感兴趣的内容
+    - 社区互动增加用户参与感和粘性
+    - 用户个人中心提供个性化服务和历史记录管理
+
+
+
+## 各页面对标竞品截图梳理
+
+
+- 创作页面对标
+ ![](./assets/img/sjym.jpg)
+
+- 推荐详情页面对标
+ ![](./assets/img/tjym.jpg)
+
+- 社区页面对标 
+![](./assets/img/sqym.jpg)

+ 2 - 1
angular.json

@@ -136,7 +136,8 @@
   "cli": {
     "schematicCollections": [
       "@ionic/angular-toolkit"
-    ]
+    ],
+    "analytics": false
   },
   "schematics": {
     "@ionic/angular-toolkit:component": {

BIN
assets/img/chuangzuo.webp


BIN
assets/img/douban.png


BIN
assets/img/sjym.jpg


BIN
assets/img/sqym.jpg


BIN
assets/img/tjym.jpg


BIN
assets/img/wenku.webp


BIN
assets/img/xcz.png


BIN
assets/img/xiangqing.webp


BIN
douban.png


BIN
imag/sqym.jpg


+ 141 - 3
package-lock.json

@@ -22,8 +22,11 @@
         "@capacitor/keyboard": "6.0.1",
         "@capacitor/status-bar": "6.0.0",
         "@ionic/angular": "^8.0.0",
+        "@ionic/storage-angular": "^4.0.0",
         "ionicons": "^7.0.0",
+        "parse": "^5.2.0",
         "rxjs": "~7.8.0",
+        "swiper": "^11.1.4",
         "tslib": "^2.3.0",
         "zone.js": "~0.14.2"
       },
@@ -2522,6 +2525,18 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@babel/runtime-corejs3": {
+      "version": "7.24.6",
+      "resolved": "https://registry.npmmirror.com/@babel/runtime-corejs3/-/runtime-corejs3-7.24.6.tgz",
+      "integrity": "sha512-tbC3o8uHK9xMgMsvUm9qGqxVpbv6yborMBLbDteHIc7JDNHsTV0vDMQ5j1O1NXvO+BDELtL9KgoWYaUVIVGt8w==",
+      "dependencies": {
+        "core-js-pure": "^3.30.2",
+        "regenerator-runtime": "^0.14.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
     "node_modules/@babel/template": {
       "version": "7.24.7",
       "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.24.7.tgz",
@@ -3520,6 +3535,27 @@
         "tslib": "^2.1.0"
       }
     },
+    "node_modules/@ionic/storage": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/@ionic/storage/-/storage-4.0.0.tgz",
+      "integrity": "sha512-3N21P19Xk6cICLnSXZ3ilRqbSXAGSFeIF3HNqz+1kARcm0UFT/vwmZreaXtFyq437vvEWOfJ2enlj3JHLKS0FA==",
+      "dependencies": {
+        "localforage": "^1.9.0"
+      }
+    },
+    "node_modules/@ionic/storage-angular": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/@ionic/storage-angular/-/storage-angular-4.0.0.tgz",
+      "integrity": "sha512-FeSmCMCm1bMRfu5TFSqLtjdfEo/dLLUhLIrPmbhSYomVZdV/dNn4mBZv9SabyxSqn4bF31hw40y+4buhG+durQ==",
+      "dependencies": {
+        "@ionic/storage": "^4.0.0",
+        "tslib": "^2.3.0"
+      },
+      "peerDependencies": {
+        "@angular/core": "*",
+        "rxjs": "*"
+      }
+    },
     "node_modules/@ionic/utils-array": {
       "version": "2.1.5",
       "resolved": "https://registry.npmmirror.com/@ionic/utils-array/-/utils-array-2.1.5.tgz",
@@ -7369,6 +7405,16 @@
         "url": "https://opencollective.com/core-js"
       }
     },
+    "node_modules/core-js-pure": {
+      "version": "3.37.1",
+      "resolved": "https://registry.npmmirror.com/core-js-pure/-/core-js-pure-3.37.1.tgz",
+      "integrity": "sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==",
+      "hasInstallScript": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/core-js"
+      }
+    },
     "node_modules/core-util-is": {
       "version": "1.0.3",
       "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
@@ -7513,6 +7559,12 @@
         "node": ">= 8"
       }
     },
+    "node_modules/crypto-js": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
+      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
+      "optional": true
+    },
     "node_modules/css-loader": {
       "version": "7.1.1",
       "resolved": "https://registry.npmmirror.com/css-loader/-/css-loader-7.1.1.tgz",
@@ -10091,6 +10143,11 @@
         "postcss": "^8.1.0"
       }
     },
+    "node_modules/idb-keyval": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/idb-keyval/-/idb-keyval-6.2.1.tgz",
+      "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg=="
+    },
     "node_modules/ieee754": {
       "version": "1.2.1",
       "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
@@ -10145,6 +10202,11 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/immediate": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
+      "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
+    },
     "node_modules/immutable": {
       "version": "4.3.6",
       "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.6.tgz",
@@ -11688,6 +11750,14 @@
         }
       }
     },
+    "node_modules/lie": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/lie/-/lie-3.1.1.tgz",
+      "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
+      "dependencies": {
+        "immediate": "~3.0.5"
+      }
+    },
     "node_modules/lines-and-columns": {
       "version": "2.0.4",
       "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-2.0.4.tgz",
@@ -11740,6 +11810,14 @@
         "node": ">= 12.13.0"
       }
     },
+    "node_modules/localforage": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmmirror.com/localforage/-/localforage-1.10.0.tgz",
+      "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
+      "dependencies": {
+        "lie": "3.1.1"
+      }
+    },
     "node_modules/locate-path": {
       "version": "6.0.0",
       "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz",
@@ -13382,6 +13460,25 @@
         "node": ">=6"
       }
     },
+    "node_modules/parse": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmmirror.com/parse/-/parse-5.2.0.tgz",
+      "integrity": "sha512-FoD3kcLAQCw/2J1984sl3GUBzbHE2tA9mUcyw/EBWZ46WVZTzV+kjnA5tttXyzN4uodt21wSluzjbGnyLqreKw==",
+      "dependencies": {
+        "@babel/runtime-corejs3": "7.24.6",
+        "idb-keyval": "6.2.1",
+        "react-native-crypto-js": "1.0.0",
+        "uuid": "10.0.0",
+        "ws": "8.17.1",
+        "xmlhttprequest": "1.8.0"
+      },
+      "engines": {
+        "node": ">=18 <21"
+      },
+      "optionalDependencies": {
+        "crypto-js": "4.2.0"
+      }
+    },
     "node_modules/parse-imports": {
       "version": "2.1.1",
       "resolved": "https://registry.npmmirror.com/parse-imports/-/parse-imports-2.1.1.tgz",
@@ -13434,6 +13531,18 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/parse/node_modules/uuid": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmmirror.com/uuid/-/uuid-10.0.0.tgz",
+      "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
+      "funding": [
+        "https://github.com/sponsors/broofa",
+        "https://github.com/sponsors/ctavan"
+      ],
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
     "node_modules/parse5": {
       "version": "7.1.2",
       "resolved": "https://registry.npmmirror.com/parse5/-/parse5-7.1.2.tgz",
@@ -14075,6 +14184,11 @@
       "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
       "dev": true
     },
+    "node_modules/react-native-crypto-js": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/react-native-crypto-js/-/react-native-crypto-js-1.0.0.tgz",
+      "integrity": "sha512-FNbLuG/HAdapQoybeZSoes1PWdOj0w242gb+e1R0hicf3Gyj/Mf8M9NaED2AnXVOX01b2FXomwUiw1xP1K+8sA=="
+    },
     "node_modules/readable-stream": {
       "version": "3.6.2",
       "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
@@ -14140,8 +14254,7 @@
     "node_modules/regenerator-runtime": {
       "version": "0.14.1",
       "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
-      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
-      "dev": true
+      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
     },
     "node_modules/regenerator-transform": {
       "version": "0.15.2",
@@ -15482,6 +15595,24 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/swiper": {
+      "version": "11.1.4",
+      "resolved": "https://registry.npmmirror.com/swiper/-/swiper-11.1.4.tgz",
+      "integrity": "sha512-1n7kbYJB2dFEpUHRFszq7gys/ofIBrMNibwTiMvPHwneKND/t9kImnHt6CfGPScMHgI+dWMbGTycCKGMoOO1KA==",
+      "funding": [
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/swiperjs"
+        },
+        {
+          "type": "open_collective",
+          "url": "http://opencollective.com/swiper"
+        }
+      ],
+      "engines": {
+        "node": ">= 4.7.0"
+      }
+    },
     "node_modules/symbol-observable": {
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/symbol-observable/-/symbol-observable-4.0.0.tgz",
@@ -17326,7 +17457,6 @@
       "version": "8.17.1",
       "resolved": "https://registry.npmmirror.com/ws/-/ws-8.17.1.tgz",
       "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
-      "dev": true,
       "engines": {
         "node": ">=10.0.0"
       },
@@ -17374,6 +17504,14 @@
         "node": ">=8.0"
       }
     },
+    "node_modules/xmlhttprequest": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmmirror.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
+      "integrity": "sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/y18n": {
       "version": "5.0.8",
       "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",

+ 3 - 0
package.json

@@ -27,8 +27,11 @@
     "@capacitor/keyboard": "6.0.1",
     "@capacitor/status-bar": "6.0.0",
     "@ionic/angular": "^8.0.0",
+    "@ionic/storage-angular": "^4.0.0",
     "ionicons": "^7.0.0",
+    "parse": "^5.2.0",
     "rxjs": "~7.8.0",
+    "swiper": "^11.1.4",
     "tslib": "^2.3.0",
     "zone.js": "~0.14.2"
   },

BIN
sjym.jpg


BIN
sqym.jpg


+ 27 - 2
src/app/app-routing.module.ts

@@ -1,11 +1,35 @@
 import { NgModule } from '@angular/core';
 import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
-
+import { authGuard } from 'src/modules/user/auth.guard';
 const routes: Routes = [
   {
     path: '',
     loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule)
-  }
+  },
+  {
+
+    path: 'user',
+    loadChildren: () => import('../modules/user/user.module').then(m => m.UserModule)
+  },
+  {
+    path: 'dongt',
+    canActivate:[authGuard],
+    loadChildren: () => import('../modules/dongt/dongt.module').then( m => m.DongtPageModule)
+  },
+  {
+    path: 'favorites',
+    canActivate:[authGuard],
+    loadChildren: () => import('../modules/favorites/favorites.module').then( m => m.FavoritesPageModule)
+  },
+ 
+  {
+    path: 'album',
+    canActivate:[authGuard],
+    loadChildren: () => import('../modules/album/album.module').then( m => m.AlbumPageModule)
+  },
+
+  
+
 ];
 @NgModule({
   imports: [
@@ -14,3 +38,4 @@ const routes: Routes = [
   exports: [RouterModule]
 })
 export class AppRoutingModule {}
+

+ 6 - 0
src/app/app.component.ts

@@ -1,3 +1,7 @@
+import Parse from "parse";
+Parse.initialize("dev"); // 设置applicationId
+Parse.serverURL = "http://web2023.fmode.cn:9999/parse"; 
+
 import { Component } from '@angular/core';
 
 @Component({
@@ -5,6 +9,8 @@ import { Component } from '@angular/core';
   templateUrl: 'app.component.html',
   styleUrls: ['app.component.scss'],
 })
+
 export class AppComponent {
   constructor() {}
+  
 }

+ 103 - 12
src/app/tab1/tab1.page.html

@@ -1,17 +1,108 @@
-<ion-header [translucent]="true">
-  <ion-toolbar>
-    <ion-title>
-      Tab 1
-    </ion-title>
+
+<ion-header>
+  <ion-toolbar color="primary">
+    <ion-title>首页</ion-title>
   </ion-toolbar>
 </ion-header>
 
-<ion-content [fullscreen]="true">
-  <ion-header collapse="condense">
-    <ion-toolbar>
-      <ion-title size="large">Tab 1</ion-title>
-    </ion-toolbar>
-  </ion-header>
+<ion-content color="light">
+  <!-- 搜索栏 -->
+  <ion-searchbar></ion-searchbar>
+
+  <!-- 分类选项 -->
+  <ion-segment>
+    <ion-segment-button value="movies">
+      影视
+    </ion-segment-button>
+    <ion-segment-button value="books">
+      读书
+    </ion-segment-button>
+    <ion-segment-button value="music">
+      音乐
+    </ion-segment-button>
+    <ion-segment-button value="games">
+      游戏
+    </ion-segment-button>
+    <ion-segment-button value="outdoor">
+      户外
+    </ion-segment-button>
+  </ion-segment>
+  <!-- 内容推荐 -->
+  <ion-card style="max-width: 100%;  height: 70px;"> 
+    <!-- Swiper轮播图 手动创建 -->
+<!-- Slider main container -->
+
+<div #slide1Comp class="swiper">
+<!-- Additional required wrapper -->
+<div class="swiper-wrapper">
+  <!-- Slides -->
+  <div class="swiper-slide"> <ion-img src="assets/image/111.jpg"></ion-img></div>
+  <div class="swiper-slide"><ion-img src="assets/image/lb2.jpg"></ion-img></div>
+  <div class="swiper-slide"><ion-img src="assets/image/lb3.jpg"></ion-img></div>
+</div>
+<!-- If we need pagination -->
+<div class="swiper-pagination"></div>
+
+<!-- If we need navigation buttons -->
+<div class="swiper-button-prev"></div>
+<div class="swiper-button-next"></div>
+
+<!-- If we need scrollbar -->
+<div class="swiper-scrollbar"></div>
+</div>
+    <ion-card-content>
+      
+      <!-- 这里放个性推荐的内容 -->
+    </ion-card-content>
+  </ion-card>
+  <ion-row>
+    <!-- 放大的四个框 -->
+    <ion-col size="6">
+      <ion-card>
+        <ion-card-header>
+          实时热门电影
+        </ion-card-header>
+        <ion-card-content>
+          <!-- 这里放热门电影的内容 -->
+          <ion-img src="assets/image/movie.jpg"></ion-img>
+        </ion-card-content>
+      </ion-card>
+    </ion-col>
+
+    <ion-col size="6">
+      <ion-card>
+        <ion-card-header>
+          国内即将上映
+        </ion-card-header>
+        <ion-card-content>
+          <!-- 这里放国内即将上映的内容 -->
+          <ion-img src="assets/image/upcoming.jpg"></ion-img>
+        </ion-card-content>
+      </ion-card>
+    </ion-col>
+
+    <ion-col size="6">
+      <ion-card>
+        <ion-card-header>
+          全球值得期待
+        </ion-card-header>
+        <ion-card-content>
+          <!-- 这里放全球值得期待的内容 -->
+          <ion-img src="assets/image/global.jpg"></ion-img>
+        </ion-card-content>
+      </ion-card>
+    </ion-col>
 
-  <app-explore-container name="Tab 1 page"></app-explore-container>
+    <ion-col size="6">
+      <ion-card>
+        <ion-card-header>
+          榜单Top100
+        </ion-card-header>
+        <ion-card-content>
+          <!-- 这里放新游戏发布的内容 -->
+          <ion-img src="assets/image/top.jpg"></ion-img>
+        </ion-card-content>
+      </ion-card>
+    </ion-col>
+  </ion-row>
 </ion-content>

+ 29 - 0
src/app/tab1/tab1.page.scss

@@ -0,0 +1,29 @@
+.rslides {
+    position: relative;
+    list-style: none;
+    overflow: hidden;
+    padding: 0;
+    margin: 0;
+    }
+  .rslides li {
+    -webkit-backface-visibility: hidden;
+    position: absolute;
+    display: none;
+    width: 100%;
+    left: 0;
+    top: 0;
+    }
+  
+  .rslides li:first-child {
+    position: relative;
+    display: block;
+    float: left;
+    }
+  
+  .rslides img {
+       display: block;
+      height: auto;
+      float: left;
+      border: 0;
+      width: 100%;
+    }

+ 37 - 3
src/app/tab1/tab1.page.ts

@@ -1,12 +1,46 @@
-import { Component } from '@angular/core';
+
+import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
+
+import Swiper from 'swiper';
 
 @Component({
   selector: 'app-tab1',
   templateUrl: 'tab1.page.html',
   styleUrls: ['tab1.page.scss']
 })
-export class Tab1Page {
+export class Tab1Page implements OnInit {
+  @ViewChild("slide1Comp") slide1Comp:ElementRef |undefined
+  constructor() { }
+
+  ngOnInit() {
+    // 延迟500毫秒加载
+    setTimeout(() => {
+      this.initSwiper();
+    }, 500);
+  }
 
-  constructor() {}
+  // 初始化轮播图
+  slide1:Swiper|undefined
+  initSwiper(){
+    console.log(this.slide1Comp)
+    this.slide1 = new Swiper(this.slide1Comp?.nativeElement, {
+      loop:true,
+      autoplay:{
+
+        delay:200,
+      },
+      mousewheel: {
+        forceToAxis: true,
+      },
+      pagination:{
+        el:".swiper-pagination",
+        clickable:true
+      }
+    });
+    setInterval(()=>{
+      this.slide1?.slideNext();
+    },1000)
+  }
 
 }
+

+ 6 - 1
src/app/tab2/tab2-routing.module.ts

@@ -6,7 +6,8 @@ const routes: Routes = [
   {
     path: '',
     component: Tab2Page,
-  }
+  },
+  
 ];
 
 @NgModule({
@@ -14,3 +15,7 @@ const routes: Routes = [
   exports: [RouterModule]
 })
 export class Tab2PageRoutingModule {}
+
+
+
+

+ 94 - 10
src/app/tab2/tab2.page.html

@@ -1,17 +1,101 @@
 <ion-header [translucent]="true">
-  <ion-toolbar>
+  <ion-toolbar color="primary">
+    
     <ion-title>
-      Tab 2
+      社区
     </ion-title>
   </ion-toolbar>
 </ion-header>
 
-<ion-content [fullscreen]="true">
-  <ion-header collapse="condense">
-    <ion-toolbar>
-      <ion-title size="large">Tab 2</ion-title>
-    </ion-toolbar>
-  </ion-header>
+<ion-content color="light">
+  <!-- 用户分享内容区域 -->
+  <ion-card color="light">
+    <ion-card-header>
+      <ion-title class="ion-text-left ion-text-large" color="tertiary">用户分享内容 <ion-icon name="fish-outline"></ion-icon></ion-title>
+     
+    </ion-card-header>
+    <ion-card-content>
+      <!-- 示例数据:用户1分享的内容 -->
+      <ion-item>
+        <ion-avatar slot="start">
+          <ion-img src="assets/image/tx1.jpg"style="position: absolute; width: 50px; height: 50px; margin-top:-70px; margin-left:-3px;"></ion-img>
+        </ion-avatar>
+        <ion-label>
+          <h2>朱迪</h2>
+          <p>Try everything.I just won't give up</p>
+          <div style="display: flex;">
+            <ion-img src="assets/image/zd2.jpg" style="max-width: 50%;"></ion-img> <!-- 第一张图片 -->
+            <ion-img src="assets/image/zd1.jpg" style="max-width: 45%;"></ion-img> <!-- 第二张图片 -->
+          </div>
+        </ion-label>
+        <ion-icon name="heart" slot="end" color="primary" (click)="toggleIconColor($event)"></ion-icon>
+      </ion-item>
+      <!-- 其他用户分享的内容 -->
+    </ion-card-content>
+  </ion-card>
 
-  <app-explore-container name="Tab 2 page"></app-explore-container>
-</ion-content>
+  <!-- 用户评论与点赞区域 -->
+  <ion-card color="light">
+    <ion-card-header>
+      <ion-title class="ion-text-left ion-text-large" color="tertiary">用户评论与点赞<ion-icon name="thumbs-up-outline"></ion-icon></ion-title>
+    </ion-card-header>
+    <ion-card-content>
+      <!-- 示例数据:用户2的评论 -->
+      <ion-item>
+        <ion-label>
+          <h2>尼克</h2>
+          <p>No matter what type of animal you are ,change start with you </p>
+        </ion-label>
+      </ion-item>
+      <!-- 用户点赞信息 -->
+      <ion-item>
+        <ion-icon name="heart" slot="end" color="primary"style="position: absolute;  left: 8px;" (click)="toggleIconColor($event)"></ion-icon>
+        <ion-icon name="chatbox-ellipses-outline" slot="start" style="position: absolute; left: 160px;" (click)="showCommentArea()"></ion-icon>
+        <ion-icon name="open-outline"slot="start"style="position: absolute;  left: 290px;"></ion-icon>
+      </ion-item>
+      <ion-item *ngIf="showCommentArea">
+        <ion-input placeholder="输入评论内容" [(ngModel)]="newComment"></ion-input>
+        <ion-button (click)="addComment()">添加评论</ion-button>
+      </ion-item>
+    </ion-card-content>
+  </ion-card>
+  
+
+  <!-- 精彩活动推荐区域 -->
+  <ion-card color="light">
+    <ion-card-header>
+      <ion-title class="ion-text-left ion-text-large" color="tertiary">精彩活动推荐<ion-icon name="bonfire-outline"></ion-icon></ion-title>
+    </ion-card-header>
+    <ion-card-content>
+      <!-- 示例数据:精彩活动1 -->
+      <ion-item>
+        <ion-thumbnail slot="start">
+          <ion-img src="assets/image/mzt.jpg"></ion-img>
+        </ion-thumbnail>
+        <ion-label>
+          <h2>法语原版音乐剧《摇滚莫扎特》</h2>
+          <p>天才音乐家的人生传记,摇滚与古典的大胆碰撞</p>
+        </ion-label>
+      </ion-item>
+      <!-- 其他精彩活动推荐 -->
+    </ion-card-content>
+  </ion-card>
+
+  <!-- 热门话题讨论区域 -->
+  <ion-card color="light">
+    <ion-card-header>
+      <ion-title class="ion-text-left ion-text-large" color="tertiary">热门话题讨论<ion-icon name="ribbon-outline"></ion-icon></ion-title>
+    </ion-card-header>
+    <ion-card-content>
+      <!-- 示例数据:话题1 -->
+      <ion-item>
+        <ion-label>史密斯先生—请允许我这样介绍他一位隐士,一位孤僻的绅士
+          他把所有的记忆都压在那隐秘的城堡将之掩盖在房间腐的尘土下
+          却又从那渡鸦般的灰色瞳孔中流露出来
+          从硝烟中活下来的史密斯先生这位二十世纪初的怪人被嫉妒的天才他在隐藏些什么?</ion-label>
+      </ion-item>
+      
+      <!-- 其他热门话题讨论 -->
+    </ion-card-content>
+  </ion-card>
+</ion-content>

+ 20 - 1
src/app/tab2/tab2.page.ts

@@ -1,12 +1,31 @@
 import { Component } from '@angular/core';
-
+import Parse from "parse";
 @Component({
   selector: 'app-tab2',
   templateUrl: 'tab2.page.html',
   styleUrls: ['tab2.page.scss']
 })
 export class Tab2Page {
+  showComment: boolean = false;
+  newComment: string = '';
 
   constructor() {}
 
+  toggleIconColor(event: any) {
+    const icon = event.target;
+    if (icon.getAttribute('color') === 'danger') {
+      icon.setAttribute('color', 'primary');
+    } else {
+      icon.setAttribute('color', 'danger');
+    }
+  }
+  showCommentArea(){
+    console.log("showCommentArea")
+    this.showComment=true
+  }
+
+  addComment() {
+    // 添加评论的逻辑
+  }
 }
+

+ 85 - 8
src/app/tab3/tab3.page.html

@@ -1,17 +1,94 @@
 <ion-header [translucent]="true">
-  <ion-toolbar>
+  <ion-toolbar color="primary">
     <ion-title>
-      Tab 3
+      我的
     </ion-title>
   </ion-toolbar>
 </ion-header>
 
 <ion-content [fullscreen]="true">
-  <ion-header collapse="condense">
-    <ion-toolbar>
-      <ion-title size="large">Tab 3</ion-title>
-    </ion-toolbar>
-  </ion-header>
+  <!-- 顶部照片部分 -->
+  <div style="position: relative;">
+    <img src="assets/image/bj1.jpg" style="width: 100%; height: 33vh;" />
+    <div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">
+    <ion-avatar>
+        <ion-img [src]="user?.id ? 'assets/image/tx2.jpg' : 'assets/image/wdl.jpg'"></ion-img>
+      </ion-avatar>
+    </div>
+    <div style="position: absolute; top: 0; right: 0;">
+      <ion-button *ngIf="!user?.id" fill="clear" routerLink="/user/login"color="tertiary" style="font-size: 20px; font-weight: bold;">登录</ion-button>
+ </div>
+ <div style="position: absolute; bottom: 0; right: 0;">
+ <ion-button *ngIf="user?.id" fill="clear" routerLink="/user/edit/info"color="tertiary" style="font-size: 12px; font-weight: bold;">编辑资料</ion-button>
+</div>
+    <div style="position: absolute; bottom: 0; left: 10px;">
+      <p style="line-height: -5; font-weight: bold; color: rgb(255, 253, 253); display: flex; align-items: center;">
+        <ion-card-title style="color: #f8f2f2; font-weight: bold;">{{user?.get("username") || '未登录'}}</ion-card-title>
+        <ion-icon name="bonfire-outline" style="margin-left: 5px;"></ion-icon>
+      </p>
+      <p>
+        <ion-card-subtitle *ngIf="user?.id" style="color: rgb(243, 243, 248);">{{user?.get("name")}}-{{user?.get("gender")}}</ion-card-subtitle>
+      </p>
+      <p>
+        <ion-card-subtitle *ngIf="user?.id" style="color: rgb(243, 243, 248);">{{user?.get("gxqm")}}</ion-card-subtitle>
+      </p>
+    </div>
+  </div>
 
-  <app-explore-container name="Tab 3 page"></app-explore-container>
+  <!-- 下方三个组件:主页、动态、相册 -->
+  <ion-row>
+    <ion-col>
+      <ion-button expand="full" routerLink="/dongt" class="custom-button">动态</ion-button>
+    </ion-col>
+    <ion-col>
+      <ion-button expand="full" routerLink="/favorites" class="custom-button">收藏</ion-button>
+    </ion-col>
+    <ion-col>
+      <ion-button expand="full" routerLink="/album" class="custom-button">相册</ion-button>
+    </ion-col>
+  </ion-row>
+
+  <ion-card>
+    <ion-card-header style="display: flex; flex-direction: row; align-items: center;">
+      <ion-icon name="book-outline" style="margin-right: 5px;"></ion-icon> 
+      <span>创建我的书影音</span>
+    </ion-card-header>
+    <ion-card-content>
+      添加你最爱的影视,它将会在你的个人主页展示。
+    </ion-card-content>
+  </ion-card>
+
+  <ion-card>
+    <ion-card-header style="display: flex; flex-direction: row; align-items: center;">
+      <ion-icon name="film" style="margin-right: 5px;"></ion-icon> 
+      <span>快速记录我看过的影视</span>
+    </ion-card-header>
+    <ion-card-content>
+     让我们来看看你看过那些影片吧!
+    </ion-card-content>
+  </ion-card>
+
+  <ion-card>
+    <ion-card-header style="display: flex; flex-direction: row; align-items: center;">
+      <ion-icon name="earth-outline" style="margin-right: 5px;"></ion-icon> 
+      <span> 我的社区</span>
+    </ion-card-header>
+    <ion-card-content>
+      快去社区分享你有趣的故事吧!
+    </ion-card-content>
+  </ion-card>
+
+  <ion-card>
+    <ion-card-header style="display: flex; flex-direction: row; align-items: center;">
+      <ion-icon name="heart-circle-outline" style="margin-right: 5px;"></ion-icon> 
+      <span> 创作灵感</span>
+    </ion-card-header>
+    <ion-card-content>
+      拯救没灵感的你,让我们来探索创意的潜力吧!
+    </ion-card-content>
+    <ion-card>
+      <ion-button *ngIf="user?.id" fill="clear" (click)="logout()">登出</ion-button>
+    </ion-card>
+  </ion-card>
 </ion-content>
+

+ 17 - 2
src/app/tab3/tab3.page.ts

@@ -1,5 +1,6 @@
 import { Component } from '@angular/core';
-
+// 由于Parse本身是js库,在ts中加载需要通过 * as Parse转换一下
+import Parse from "parse"
 @Component({
   selector: 'app-tab3',
   templateUrl: 'tab3.page.html',
@@ -7,6 +8,20 @@ import { Component } from '@angular/core';
 })
 export class Tab3Page {
 
-  constructor() {}
+  constructor() {
+   
+  }
 
+  // 由于Parse.User.current()是随着localStorage变化的属性
+  // 为了避免首次复制后用户状态变化,页面不同步,通过get方法实现实时获取
+  user:Parse.User|undefined
+  async ngOnInit() {
+      this.user = await Parse.User.current()
+      setInterval(async ()=>{
+      this.user = await Parse.User.current()
+    },1000)
+  }
+  logout(){
+    Parse.User.logOut();
+  }
 }

+ 3 - 0
src/app/tabs/tabs-routing.module.ts

@@ -19,6 +19,9 @@ const routes: Routes = [
         path: 'tab3',
         loadChildren: () => import('../tab3/tab3.module').then(m => m.Tab3PageModule)
       },
+      { path: 'mine',
+    loadChildren: () => import('../../modules/user/mine/mine.module').then(m => m.MinePageModule)
+      },
       {
         path: '',
         redirectTo: '/tabs/tab1',

+ 8 - 6
src/app/tabs/tabs.page.html

@@ -2,19 +2,21 @@
 
   <ion-tab-bar slot="bottom">
     <ion-tab-button tab="tab1" href="/tabs/tab1">
-      <ion-icon aria-hidden="true" name="triangle"></ion-icon>
-      <ion-label>Tab 1</ion-label>
+      <ion-icon aria-hidden="true" name="home-outline"></ion-icon>
+      <ion-label>首页</ion-label>
     </ion-tab-button>
 
     <ion-tab-button tab="tab2" href="/tabs/tab2">
-      <ion-icon aria-hidden="true" name="ellipse"></ion-icon>
-      <ion-label>Tab 2</ion-label>
+      <ion-icon aria-hidden="true" name="earth-outline"></ion-icon>
+      <ion-label>社区</ion-label>
     </ion-tab-button>
 
     <ion-tab-button tab="tab3" href="/tabs/tab3">
-      <ion-icon aria-hidden="true" name="square"></ion-icon>
-      <ion-label>Tab 3</ion-label>
+      <ion-icon aria-hidden="true" name="person-circle-outline"></ion-icon>
+     
+      <ion-label>我的</ion-label>
     </ion-tab-button>
+
   </ion-tab-bar>
 
 </ion-tabs>

BIN
src/assets/image/111.jpg


BIN
src/assets/image/bj1.jpg


BIN
src/assets/image/dt1.jpg


BIN
src/assets/image/dt2.jpg


BIN
src/assets/image/dt3.jpg


BIN
src/assets/image/dt4.jpg


BIN
src/assets/image/dt5.jpg


BIN
src/assets/image/dt6.jpg


BIN
src/assets/image/global.jpg


BIN
src/assets/image/lb2.jpg


BIN
src/assets/image/lb3.jpg


BIN
src/assets/image/movie.jpg


BIN
src/assets/image/mzt.jpg


BIN
src/assets/image/top.jpg


BIN
src/assets/image/tx1.jpg


BIN
src/assets/image/tx2.jpg


BIN
src/assets/image/upcoming.jpg


BIN
src/assets/image/wdl.jpg


BIN
src/assets/image/zd1.jpg


BIN
src/assets/image/zd2.jpg


BIN
src/assets/image/zhudi.jpg


File diff suppressed because it is too large
+ 12 - 0
src/assets/swiper/swiper.min.css


+ 2 - 1
src/index.html

@@ -11,7 +11,8 @@
   <meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
   <meta name="format-detection" content="telephone=no" />
   <meta name="msapplication-tap-highlight" content="no" />
-
+<!-- Swiper轮播图样式 -->
+<link rel="stylesheet" href="assets/swiper/swiper.min.css">
   <link rel="icon" type="image/png" href="assets/icon/favicon.png" />
 
   <!-- add to homescreen for ios -->

+ 17 - 0
src/modules/album/album-routing.module.ts

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { AlbumPage } from './album.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: AlbumPage
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+})
+export class AlbumPageRoutingModule {}

+ 20 - 0
src/modules/album/album.module.ts

@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { AlbumPageRoutingModule } from './album-routing.module';
+
+import { AlbumPage } from './album.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    AlbumPageRoutingModule
+  ],
+  declarations: [AlbumPage]
+})
+export class AlbumPageModule {}

+ 17 - 0
src/modules/album/album.page.html

@@ -0,0 +1,17 @@
+<ion-header>
+  <ion-toolbar color="primary">
+    <ion-title>我的相册</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content>
+  <ion-grid>
+    <ion-row>
+      <ion-col size="6" *ngFor="let photo of albumPhotos">
+        <ion-img [src]="photo.imageUrl"></ion-img>
+      </ion-col>
+    </ion-row>
+  </ion-grid>
+
+  <ion-button expand="block" (click)="addPhoto()">添加图片</ion-button>
+</ion-content>

+ 0 - 0
src/modules/album/album.page.scss


+ 17 - 0
src/modules/album/album.page.spec.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { AlbumPage } from './album.page';
+
+describe('AlbumPage', () => {
+  let component: AlbumPage;
+  let fixture: ComponentFixture<AlbumPage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(AlbumPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 22 - 0
src/modules/album/album.page.ts

@@ -0,0 +1,22 @@
+import { Component } from '@angular/core';
+import { AlbumService } from '../album/album.service'; // 替换为实际的路径
+
+@Component({
+  selector: 'app-album',
+  templateUrl: 'album.page.html',
+  styleUrls: ['album.page.scss'],
+})
+export class AlbumPage {
+  constructor(private albumService: AlbumService) {}
+
+  get albumPhotos() {
+    return this.albumService.getAlbumPhotos();
+  }
+
+  addPhoto() {
+    const newPhoto = {
+      imageUrl: 'assets/image/tx1.jpg'
+    };
+    this.albumService.addPhoto(newPhoto);
+  }
+}

+ 36 - 0
src/modules/album/album.service.ts

@@ -0,0 +1,36 @@
+import { Injectable } from '@angular/core';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class AlbumService {
+  private albumPhotos = [
+    {
+      imageUrl: 'assets/image/dt1.jpg'
+    },
+    {
+      imageUrl: 'assets/image/dt5.jpg'
+    },
+    {
+      imageUrl: 'assets/image/dt3.jpg'
+    },
+    {
+        imageUrl: 'assets/image/dt4.jpg'
+      },
+      {
+        imageUrl: 'assets/image/dt2.jpg'
+      }
+  ];
+
+  constructor() {}
+
+  getAlbumPhotos() {
+    return this.albumPhotos;
+  }
+
+  addPhoto(newPhoto: { imageUrl: string; }) {
+    const randomIndex = Math.floor(Math.random() * this.albumPhotos.length); // 随机选择一个索引
+    const randomPhoto = this.albumPhotos[randomIndex];
+    this.albumPhotos.push(randomPhoto); // 将随机选择的图片添加到相册中
+  }
+}

+ 17 - 0
src/modules/dongt/dongt-routing.module.ts

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { DongtPage } from './dongt.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: DongtPage
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+})
+export class DongtPageRoutingModule {}

+ 20 - 0
src/modules/dongt/dongt.module.ts

@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { DongtPageRoutingModule } from './dongt-routing.module';
+
+import { DongtPage } from './dongt.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    DongtPageRoutingModule
+  ],
+  declarations: [DongtPage]
+})
+export class DongtPageModule {}

+ 74 - 0
src/modules/dongt/dongt.page.html

@@ -0,0 +1,74 @@
+
+<ion-header [translucent]="true">
+  <ion-toolbar color="primary">
+    <ion-buttons slot="start">
+      <ion-back-button defaultHref="/"></ion-back-button> <!-- 返回上一级按钮 -->
+    </ion-buttons>
+    <ion-title>动态</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content>
+  <ion-card>
+    <ion-card-content>
+      <!-- 示例数据:用户1分享的内容 -->
+      <ion-item>
+        <ion-avatar slot="start">
+          <ion-img src="assets/image/tx2.jpg" style="position: absolute; width: 50px; height: 50px; margin-top: -145px; margin-left: -3px;"></ion-img>
+        </ion-avatar>
+        <ion-label>
+          <ion-input value="啦啦啦"></ion-input> <!-- 将标题文本设为可编辑 -->
+          <ion-input value="山海自有归期 风雨自有相逢 "></ion-input> <!-- 将描述文本设为可编辑 -->
+          <div style="display: flex;">
+            <ion-img src="assets/image/dt1.jpg" style="max-width: 50%;"></ion-img> <!-- 第一张图片 -->
+            <ion-img src="assets/image/dt6.jpg" style="max-width: 50%;"></ion-img> <!-- 第二张图片 -->
+          </div>
+          <ion-item>
+            <ion-icon name="heart" slot="start" color="danger" style="position: absolute; left: 10px;"></ion-icon> <!-- 将heart图标设为红色 -->
+            <ion-icon name="chatbox-ellipses-outline" slot="start" style="position: absolute; left: 125px;"></ion-icon>
+            <ion-icon name="open-outline" slot="start" style="position: absolute; left: 240px;"></ion-icon>
+          </ion-item>
+        </ion-label>
+      </ion-item>
+      <ion-item>
+        <ion-avatar slot="start">
+          <ion-img src="assets/image/tx2.jpg" style="position: absolute; width: 50px; height: 50px; margin-top: -145px; margin-left: -3px;"></ion-img>
+        </ion-avatar>
+        <ion-label>
+          <ion-input value="啦啦啦"></ion-input> <!-- 将标题文本设为可编辑 -->
+          <ion-input value="Try everything. I just won't give up"></ion-input> <!-- 将描述文本设为可编辑 -->
+          <div style="display: flex;">
+            <ion-img src="assets/image/dt4.jpg" style="max-width: 50%;"></ion-img> <!-- 第一张图片 -->
+            <ion-img src="assets/image/dt3.jpg" style="max-width: 50%;"></ion-img> <!-- 第二张图片 -->
+          </div>
+          <ion-item>
+            <ion-icon name="heart" slot="start" color="danger" style="position: absolute; left: 10px;"></ion-icon> <!-- 将heart图标设为红色 -->
+            <ion-icon name="chatbox-ellipses-outline" slot="start" style="position: absolute; left: 125px;"></ion-icon>
+            <ion-icon name="open-outline" slot="start" style="position: absolute; left: 240px;"></ion-icon>
+          </ion-item>
+        </ion-label>
+      </ion-item>
+      <ion-item>
+        <ion-avatar slot="start">
+          <ion-img src="assets/image/tx2.jpg" style="position: absolute; width: 50px; height: 50px; margin-top: -145px; margin-left: -3px;"></ion-img>
+        </ion-avatar>
+        <ion-label>
+          <ion-input value="啦啦啦"></ion-input> <!-- 将标题文本设为可编辑 -->
+          <ion-input value="Try everything. I just won't give up"></ion-input> <!-- 将描述文本设为可编辑 -->
+          <div style="display: flex;">
+            <ion-img src="assets/image/dt2.jpg" style="max-width: 50%;"></ion-img> <!-- 第一张图片 -->
+            <ion-img src="assets/image/dt5.jpg" style="max-width: 50%;"></ion-img> <!-- 第二张图片 -->
+          </div>
+          <ion-item>
+            <ion-icon name="heart" slot="start" color="danger" style="position: absolute; left: 10px;"></ion-icon> <!-- 将heart图标设为红色 -->
+            <ion-icon name="chatbox-ellipses-outline" slot="start" style="position: absolute; left: 125px;"></ion-icon>
+            <ion-icon name="open-outline" slot="start" style="position: absolute; left: 240px;"></ion-icon>
+          </ion-item>
+        </ion-label>
+      </ion-item>
+      <!-- 其他用户分享的内容 -->
+    </ion-card-content>
+  </ion-card>
+</ion-content>
+
+

+ 0 - 0
src/modules/dongt/dongt.page.scss


+ 17 - 0
src/modules/dongt/dongt.page.spec.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { DongtPage } from './dongt.page';
+
+describe('ZhuyePage', () => {
+  let component: DongtPage;
+  let fixture: ComponentFixture<DongtPage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(DongtPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 26 - 0
src/modules/dongt/dongt.page.ts

@@ -0,0 +1,26 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector: 'app-dongt',
+  templateUrl: './dongt.page.html',
+  styleUrls: ['./dongt.page.scss'],
+})
+export class DongtPage {
+
+  constructor() {
+   
+  }
+
+  // 由于Parse.User.current()是随着localStorage变化的属性
+  // 为了避免首次复制后用户状态变化,页面不同步,通过get方法实现实时获取
+  user:Parse.User|undefined
+  async ngOnInit() {
+      this.user = await Parse.User.current()
+      setInterval(async ()=>{
+      this.user = await Parse.User.current()
+    },1000)
+  }
+  logout(){
+    Parse.User.logOut();
+  }
+}

+ 17 - 0
src/modules/favorites/favorites-routing.module.ts

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { FavoritesPage } from './favorites.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: FavoritesPage
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+})
+export class FavoritesPageRoutingModule {}

+ 20 - 0
src/modules/favorites/favorites.module.ts

@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { FavoritesPageRoutingModule } from './favorites-routing.module';
+
+import { FavoritesPage } from './favorites.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    FavoritesPageRoutingModule
+  ],
+  declarations: [FavoritesPage]
+})
+export class FavoritesPageModule {}

+ 23 - 0
src/modules/favorites/favorites.page.html

@@ -0,0 +1,23 @@
+<ion-header>
+  <ion-toolbar color="primary">
+    <ion-buttons slot="start">
+      <ion-back-button defaultHref="/"></ion-back-button>
+    </ion-buttons>
+    <ion-title>我的收藏</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content>
+  <ion-list>
+    <ion-item *ngFor="let item of favoriteItems">
+      <ion-thumbnail slot="start">
+        <ion-img [src]="item.imageUrl"></ion-img>
+      </ion-thumbnail>
+      <ion-label>
+        <h2>{{item.title}}</h2>
+        <p>{{item.description}}</p>
+      </ion-label>
+      <ion-button slot="end" (click)="removeFromFavorites(item)">移除</ion-button>
+    </ion-item>
+  </ion-list>
+</ion-content>

+ 0 - 0
src/modules/favorites/favorites.page.scss


+ 17 - 0
src/modules/favorites/favorites.page.spec.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { FavoritesPage } from './favorites.page';
+
+describe('FavoritesPage', () => {
+  let component: FavoritesPage;
+  let fixture: ComponentFixture<FavoritesPage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(FavoritesPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 28 - 0
src/modules/favorites/favorites.page.ts

@@ -0,0 +1,28 @@
+import { Component } from '@angular/core';
+import { FavoritesService } from '../favorites/favorites.service'; // 替换为实际的路径
+
+@Component({
+  selector: 'app-favorites',
+  templateUrl: 'favorites.page.html',
+  styleUrls: ['favorites.page.scss'],
+})
+export class FavoritesPage {
+  constructor(private favoritesService: FavoritesService) {}
+
+  get favoriteItems() {
+    return this.favoritesService.getFavoriteItems();
+  }
+
+  removeFromFavorites(item: { title: string; description: string; imageUrl: string; }) {
+    this.favoritesService.removeFromFavorites(item);
+  }
+
+  addToFavorites() {
+    const newItem = {
+      title: '新商品',
+      description: '这是新商品的描述',
+      imageUrl: 'assets/images/new-product.jpg'
+    };
+    this.favoritesService.addToFavorites(newItem);
+  }
+}

+ 58 - 0
src/modules/favorites/favorites.service.ts

@@ -0,0 +1,58 @@
+import { Injectable } from '@angular/core';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class FavoritesService {
+  addToFavorites(newItem: { title: string; description: string; imageUrl: string; }) {
+    throw new Error('Method not implemented.');
+  }
+  private favoriteItems = [
+    {
+      title: '商品1',
+      description: '这是商品1的描述',
+      imageUrl: 'assets/images/product1.jpg'
+    },
+    {
+      title: '商品2',
+      description: '这是商品2的描述',
+      imageUrl: 'assets/images/product2.jpg'
+    },
+    {
+        title: '商品2',
+        description: '这是商品2的描述',
+        imageUrl: 'assets/images/product2.jpg'
+      },
+      {
+        title: '商品2',
+        description: '这是商品2的描述',
+        imageUrl: 'assets/images/product2.jpg'
+      },
+      {
+        title: '商品2',
+        description: '这是商品2的描述',
+        imageUrl: 'assets/images/product2.jpg'
+      },
+      {
+        title: '商品2',
+        description: '这是商品2的描述',
+        imageUrl: 'assets/images/product2.jpg'
+      },
+      {
+        title: '商品2',
+        description: '这是商品2的描述',
+        imageUrl: 'assets/images/product2.jpg'
+      },
+  ];
+
+  getFavoriteItems() {
+    return this.favoriteItems;
+  }
+
+  removeFromFavorites(item: { title: string; description: string; imageUrl: string; }) {
+    const index = this.favoriteItems.indexOf(item);
+    if (index > -1) {
+      this.favoriteItems.splice(index, 1);
+    }
+  }
+}

+ 18 - 0
src/modules/user/auth.guard.spec.ts

@@ -0,0 +1,18 @@
+import { TestBed } from '@angular/core/testing';
+import { CanActivateFn } from '@angular/router';
+
+import { authGuard } from './auth.guard';
+
+describe('authGuard', () => {
+  const executeGuard: CanActivateFn = (...guardParameters) => 
+      TestBed.runInInjectionContext(() => authGuard(...guardParameters));
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+  });
+
+  it('should be created', () => {
+    expect(executeGuard).toBeTruthy();
+  });
+  
+});

+ 10 - 0
src/modules/user/auth.guard.ts

@@ -0,0 +1,10 @@
+import { CanActivateFn } from '@angular/router';
+import Parse from "parse";
+export const authGuard: CanActivateFn = (route, state) => {
+  let user = Parse.User.current()
+  if(user?.id){
+    return true
+  }else{
+    return false;
+  }
+};

+ 17 - 0
src/modules/user/edit-info/edit-info-routing.module.ts

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { EditInfoPage } from './edit-info.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: EditInfoPage
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+})
+export class EditInfoPageRoutingModule {}

+ 20 - 0
src/modules/user/edit-info/edit-info.module.ts

@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { EditInfoPageRoutingModule } from './edit-info-routing.module';
+
+import { EditInfoPage } from './edit-info.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    EditInfoPageRoutingModule
+  ],
+  declarations: [EditInfoPage]
+})
+export class EditInfoPageModule {}

+ 47 - 0
src/modules/user/edit-info/edit-info.page.html

@@ -0,0 +1,47 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title>资料编辑</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <ion-header collapse="condense">
+    <ion-toolbar>
+      <ion-title size="large">资料编辑</ion-title>
+    </ion-toolbar>
+  </ion-header>
+
+  <ion-card>
+    <ion-card-header>
+      <ion-card-title>{{currentUser?.get('username')}} - {{currentUser?.id}}</ion-card-title>
+    </ion-card-header>
+    <ion-card-content>
+      <ion-list>
+        <ion-item>
+          <ion-input label="城市" type="text" [(ngModel)]="userInfo.name"></ion-input>
+        </ion-item>
+        <ion-item>
+          <ion-input label="个性签名" type="tel" [(ngModel)]="userInfo.gxqm"></ion-input>
+        </ion-item>
+        <ion-item>
+          <ion-select label="性别" [(ngModel)]="userInfo.gender">
+            <ion-select-option value="男">男</ion-select-option>
+            <ion-select-option value="女">女</ion-select-option>
+          </ion-select>
+        </ion-item>
+        <ion-item>
+          <ion-label>生日</ion-label>
+          <ion-datetime-button datetime="birthday"></ion-datetime-button>
+          <ion-modal [keepContentsMounted]="true">
+            <ng-template>
+              <ion-datetime id="birthday" displayFormat="MM/DD/YYYY" [(ngModel)]="userInfo.birthday"></ion-datetime>
+            </ng-template>
+          </ion-modal>
+        </ion-item>
+      </ion-list>
+    </ion-card-content>
+  </ion-card>
+
+  <ion-button expand="block" (click)="save()">保存</ion-button>
+  <ion-button expand="block" (click)="cancel()">取消</ion-button>
+</ion-content>

+ 0 - 0
src/modules/user/edit-info/edit-info.page.scss


+ 17 - 0
src/modules/user/edit-info/edit-info.page.spect.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { EditInfoPage } from './edit-info.page';
+
+describe('EditInfoPage', () => {
+  let component: EditInfoPage;
+  let fixture: ComponentFixture<EditInfoPage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(EditInfoPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 58 - 0
src/modules/user/edit-info/edit-info.page.ts

@@ -0,0 +1,58 @@
+import { Component, OnInit } from '@angular/core';
+import { NavController } from '@ionic/angular';
+import * as Parse from 'parse';
+
+@Component({
+  selector: 'app-edit-info',
+  templateUrl: './edit-info.page.html',
+  styleUrls: ['./edit-info.page.scss'],
+})
+export class EditInfoPage implements OnInit {
+
+  userInfo: any = {
+    name: '',
+    mobile: '',
+    gender: '',
+    birthday: ''
+  };
+  currentUser:Parse.User|undefined
+  constructor(private navController: NavController) {}
+
+  ngOnInit() {
+    this.currentUser = Parse.User.current();
+    if (this.currentUser) {
+      // 修改uesrInfo赋值逻辑,仅加载被编辑的字段属性值
+      let json = this.currentUser.toJSON();
+      for (const key in json) {
+        if (this.userInfo.hasOwnProperty(key)) {
+          this.userInfo[key] = json[key]
+        }
+      }
+    }
+    console.log(this.userInfo)
+  }
+
+  save() {
+    this.currentUser = Parse.User.current();
+    if (this.currentUser) {
+      console.log(this.userInfo)
+      this.userInfo.birthday = this.userInfo.birthday || new Date()
+      this.userInfo.birthday = new Date(this.userInfo.birthday);
+      for (const key in this.userInfo) {
+        if (this.userInfo.hasOwnProperty(key)) {
+          this.currentUser.set(key, this.userInfo[key]);
+        }
+      }
+      this.currentUser.save().then(() => {
+        this.navController.back();
+      }).catch((error) => {
+        console.error('Error saving user data: ', error);
+      });
+    }
+  }
+
+  cancel() {
+    this.navController.back();
+  }
+
+}

+ 17 - 0
src/modules/user/login/login-routing.module.ts

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { LoginPage } from './login.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: LoginPage
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+})
+export class LoginPageRoutingModule {}

+ 20 - 0
src/modules/user/login/login.module.ts

@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { LoginPageRoutingModule } from './login-routing.module';
+
+import { LoginPage } from './login.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    LoginPageRoutingModule
+  ],
+  declarations: [LoginPage]
+})
+export class LoginPageModule {}

+ 33 - 0
src/modules/user/login/login.page.html

@@ -0,0 +1,33 @@
+<ion-header [translucent]="true">
+  <ion-toolbar color="primary">
+      <ion-title>登录/注册</ion-title>
+    </ion-toolbar>
+  </ion-header>
+  
+  <ion-content [fullscreen]="true">
+  
+    <ion-card>
+      <ion-card-header>
+        <ion-card-title>登录/注册</ion-card-title>
+      </ion-card-header>
+    
+      <ion-card-content>
+  
+        <ion-list [inset]="true">
+          <ion-item>
+            <ion-input [(ngModel)]="username" label="账号" placeholder="请输入用户名"></ion-input>
+          </ion-item>
+          <ion-item>
+            <ion-input [(ngModel)]="password" label="密码" type="password" placeholder="请输入密码"></ion-input>
+          </ion-item>
+        </ion-list>
+       
+      </ion-card-content>
+    
+      <ion-button (click)="login()" fill="clear">登录</ion-button>
+      <ion-button (click)="register()" fill="clear">注册</ion-button>
+    </ion-card>
+  
+    <!-- 新增路由返回逻辑,执行back函数 -->
+    <ion-button expand="block" (click)="back()">返回</ion-button>
+  </ion-content>

+ 0 - 0
src/modules/user/login/login.page.scss


+ 17 - 0
src/modules/user/login/login.page.spec.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { LoginPage } from './login.page';
+
+describe('LoginPage', () => {
+  let component: LoginPage;
+  let fixture: ComponentFixture<LoginPage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(LoginPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 93 - 0
src/modules/user/login/login.page.ts

@@ -0,0 +1,93 @@
+import { Component, OnInit } from '@angular/core';
+import { AlertController, NavController } from '@ionic/angular';
+import * as Parse from "parse"
+// 引用Router服务
+@Component({
+  selector: 'app-login',
+  templateUrl: './login.page.html',
+  styleUrls: ['./login.page.scss'],
+})
+export class LoginPage implements OnInit {
+
+  username:string = ""
+  password:string = ""
+  constructor(
+    // 新增:Router服务,用于路由跳转
+    private navCtrl:NavController,
+    private alertController:AlertController
+  ) { }
+
+  ngOnInit() {
+  }
+
+  async login(){
+    let user
+    try {
+      user = await Parse.User.logIn(this.username,this.password)
+    } catch (error:any) {
+      let message:string = ""
+      // 新增提示词详情,根据Parse.User.login方法返回的不同英文提示词,增加对应的中文内容转换
+      if(error?.message.indexOf("is required")>-1){
+        message = "必须输入账号或邮箱"
+      }
+      if(error?.message.indexOf("Invalid username")>-1){
+        message = "账号或密码错误,请检查"
+      }
+      this.presentAlert({
+        header:"登录失败",
+        subHeader:"状态码:"+error.code,
+        message:message || error.message
+      })
+    }
+    console.log(user)
+    if(user?.id){
+      this.navCtrl.back()
+    }
+  }
+  async register(){
+    let user = new Parse.User()
+    user.set("username",this.username)
+    user.set("password",this.password)
+    try {
+        let result = await user.signUp();
+        console.log(result)
+        if(result?.id){
+          this.navCtrl.back()
+        }
+        // Hooray! Let them use the app now.
+    } catch (error:any) {
+        // 新增提示词详情,根据Parse.User.signUp方法返回的不同英文提示词,增加对应的中文内容转换
+        let message:string = ""
+        if(error?.message.indexOf("already exists")>-1){
+          message = "该账号已存在请修改后重试"
+        }
+        if(error?.message.indexOf("empty")>-1){
+          message = "账号不能为空请输入后重试"
+        }
+        this.presentAlert({
+          header:"注册失败",
+          subHeader:"状态码:"+error.code,
+          message:message || error.message
+        })
+    }
+  }
+
+  async presentAlert(options:{header:string,subHeader:string,message:string}) {
+    const alert = await this.alertController.create({
+      header: options?.header,
+      subHeader: options?.subHeader,
+      message: options?.message,
+      buttons: ['好的'],
+    });
+
+    await alert.present();
+  }
+
+  /**
+   * 返回上级页面函数
+   * @desc
+   */
+  back(){
+    this.navCtrl.back()
+  }
+}

+ 17 - 0
src/modules/user/mine/mine-routing.module.ts

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { MinePage } from './mine.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: MinePage
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+})
+export class MinePageRoutingModule {}

+ 20 - 0
src/modules/user/mine/mine.module.ts

@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { MinePageRoutingModule } from './mine-routing.module';
+
+import { MinePage } from './mine.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    MinePageRoutingModule
+  ],
+  declarations: [MinePage]
+})
+export class MinePageModule {}

+ 24 - 0
src/modules/user/mine/mine.page.html

@@ -0,0 +1,24 @@
+<ion-header [translucent]="true">
+    <ion-toolbar>
+      <ion-title>我的</ion-title>
+    </ion-toolbar>
+  </ion-header>
+  
+  <ion-content [fullscreen]="true">
+  
+    <ion-card>
+      <img alt="" src="https://ionicframework.com/docs/img/demos/card-media.png" />
+      <ion-card-header>
+        <ion-card-title>{{user?.get("username") || '未登录'}}</ion-card-title>
+        <ion-card-subtitle *ngIf="!user?.id">请您登陆后继续使用</ion-card-subtitle>
+        <ion-card-subtitle *ngIf="user?.id">{{user?.get("name")}}-{{user?.get("gender")}}</ion-card-subtitle>
+      </ion-card-header>
+   
+      <!-- 新增:根据用户状态,显示登录/登出按钮,执行跳转或登出函数 -->
+      <ion-button *ngIf="!user?.id" fill="clear" routerLink="/user/login">登录</ion-button>
+      <ion-button *ngIf="user?.id" fill="clear" routerLink="/user/edit/info">编辑资料</ion-button>
+      <ion-button *ngIf="user?.id" fill="clear" (click)="logout()">登出</ion-button>
+    </ion-card>
+  
+  
+  </ion-content>

+ 0 - 0
src/modules/user/mine/mine.page.scss


+ 17 - 0
src/modules/user/mine/mine.page.spec.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { MinePage } from './mine.page';
+
+describe('MinePage', () => {
+  let component: MinePage;
+  let fixture: ComponentFixture<MinePage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(MinePage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 28 - 0
src/modules/user/mine/mine.page.ts

@@ -0,0 +1,28 @@
+import { Component, OnInit } from '@angular/core';
+// 由于Parse本身是js库,在ts中加载需要通过 * as Parse转换一下
+import Parse from "parse"
+@Component({
+  selector: 'app-mine',
+  templateUrl: './mine.page.html',
+  styleUrls: ['./mine.page.scss'],
+})
+export class MinePage implements OnInit {
+
+  constructor() {
+   
+  }
+
+  // 由于Parse.User.current()是随着localStorage变化的属性
+  // 为了避免首次复制后用户状态变化,页面不同步,通过get方法实现实时获取
+  user:Parse.User|undefined
+  async ngOnInit() {
+      this.user = await Parse.User.current()
+      setInterval(async ()=>{
+      this.user = await Parse.User.current()
+    },1000)
+  }
+  logout(){
+    Parse.User.logOut();
+  }
+
+}

+ 14 - 0
src/modules/user/user-routing.module.ts

@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+const routes: Routes = [
+    {path: 'login', loadChildren: () => import('./login/login.module').then(mod => mod.LoginPageModule)},
+    {path: 'mine', loadChildren: () => import('./mine/mine.module').then(mod => mod.MinePageModule)},
+    {path: 'edit/info', loadChildren: () => import('./edit-info/edit-info.module').then(mod => mod.EditInfoPageModule)},
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class UserRoutingModule { }

+ 14 - 0
src/modules/user/user.module.ts

@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+import { UserRoutingModule } from './user-routing.module';
+
+
+@NgModule({
+  declarations: [],
+  imports: [
+    CommonModule,
+    UserRoutingModule
+  ]
+})
+export class UserModule { }

+ 65 - 0
src/theme/variables.scss

@@ -1,2 +1,67 @@
 // For information on how to create your own theme, please see:
 // http://ionicframework.com/docs/theming/
+:root {
+	--ion-color-primary: hsl(218, 67%, 83%);
+	--ion-color-primary-rgb: 0,84,233;
+	--ion-color-primary-contrast: #ffffff;
+	--ion-color-primary-contrast-rgb: 255,255,255;
+	--ion-color-primary-shade: #92afe1;
+	--ion-color-primary-tint: #8bace4;
+
+	--ion-color-secondary: #85cfe8;
+	--ion-color-secondary-rgb: 1,99,170;
+	--ion-color-secondary-contrast: #ffffff;
+	--ion-color-secondary-contrast-rgb: 255,255,255;
+	--ion-color-secondary-shade: #015796;
+	--ion-color-secondary-tint: #1a73b3;
+
+	--ion-color-tertiary: #fdfbfe;
+	--ion-color-tertiary-rgb: 96,48,255;
+	--ion-color-tertiary-contrast: #ffffff;
+	--ion-color-tertiary-contrast-rgb: 255,255,255;
+	--ion-color-tertiary-shade: #542ae0;
+	--ion-color-tertiary-tint: #7045ff;
+
+	--ion-color-success: #2dd55b;
+	--ion-color-success-rgb: 45,213,91;
+	--ion-color-success-contrast: #000000;
+	--ion-color-success-contrast-rgb: 0,0,0;
+	--ion-color-success-shade: #28bb50;
+	--ion-color-success-tint: #42d96b;
+
+	--ion-color-warning: #ffc409;
+	--ion-color-warning-rgb: 255,196,9;
+	--ion-color-warning-contrast: #000000;
+	--ion-color-warning-contrast-rgb: 0,0,0;
+	--ion-color-warning-shade: #e0ac08;
+	--ion-color-warning-tint: #ffca22;
+
+	--ion-color-danger: #c5000f;
+	--ion-color-danger-rgb: 197,0,15;
+	--ion-color-danger-contrast: #ffffff;
+	--ion-color-danger-contrast-rgb: 255,255,255;
+	--ion-color-danger-shade: #ad000d;
+	--ion-color-danger-tint: #cb1a27;
+
+	--ion-color-light: #b4ebee;
+	--ion-color-light-rgb: 246,248,252;
+	--ion-color-light-contrast: #000000;
+	--ion-color-light-contrast-rgb: 0,0,0;
+	--ion-color-light-shade: #d8dade;
+	--ion-color-light-tint: #f7f9fc;
+
+	--ion-color-medium: #5f5f5f;
+	--ion-color-medium-rgb: 95,95,95;
+	--ion-color-medium-contrast: #ffffff;
+	--ion-color-medium-contrast-rgb: 255,255,255;
+	--ion-color-medium-shade: #545454;
+	--ion-color-medium-tint: #6f6f6f;
+
+	--ion-color-dark: #2f2f2f;
+	--ion-color-dark-rgb: 47,47,47;
+	--ion-color-dark-contrast: #ffffff;
+	--ion-color-dark-contrast-rgb: 255,255,255;
+	--ion-color-dark-shade: #292929;
+	--ion-color-dark-tint: #444444;
+
+}

BIN
tjym.jpg


+ 1 - 0
tsconfig.json

@@ -5,6 +5,7 @@
     "baseUrl": "./",
     "outDir": "./dist/out-tsc",
     "forceConsistentCasingInFileNames": true,
+    "allowSyntheticDefaultImports": true,
     "strict": true,
     "noImplicitOverride": true,
     "noPropertyAccessFromIndexSignature": true,

Some files were not shown because too many files changed in this diff