John Doe 4 月之前
父节点
当前提交
5f6092a8e8
共有 100 个文件被更改,包括 2566 次插入776 次删除
  1. 266 0
      README.md
  2. 二进制
      image/641.webp
  3. 二进制
      image/发布.jpg
  4. 二进制
      image/我的.jpg
  5. 二进制
      image/校园帮棒.png
  6. 二进制
      image/校园帮棒分析.png
  7. 二进制
      image/消息.jpg
  8. 二进制
      image/脸帮.png
  9. 二进制
      image/首页.jpg
  10. 0 0
      server/REST.js
  11. 42 0
      server/case/promise.js
  12. 110 0
      server/fmode-parse copy.js
  13. 40 0
      server/fmode-parse-test.js
  14. 110 0
      server/fmode-parse.js
  15. 112 0
      server/rest copy.js
  16. 17 0
      src/app/activity/activity-routing.module.ts
  17. 20 0
      src/app/activity/activity.module.ts
  18. 24 0
      src/app/activity/activity.page.html
  19. 36 0
      src/app/activity/activity.page.scss
  20. 5 5
      src/app/activity/activity.page.spec.ts
  21. 28 0
      src/app/activity/activity.page.ts
  22. 20 8
      src/app/app-routing.module.ts
  23. 2 1
      src/app/app.component.ts
  24. 1 0
      src/app/app.module.ts
  25. 79 0
      src/app/contact/README.md
  26. 13 0
      src/app/contact/chat/chat-routing.module.ts
  27. 7 9
      src/app/contact/chat/chat.module.ts
  28. 66 0
      src/app/contact/chat/chat.page.html
  29. 79 0
      src/app/contact/chat/chat.page.scss
  30. 26 0
      src/app/contact/chat/chat.page.spec.ts
  31. 121 0
      src/app/contact/chat/chat.page.ts
  32. 17 0
      src/app/contact/contact-detail/contact-detail-routing.module.ts
  33. 20 0
      src/app/contact/contact-detail/contact-detail.module.ts
  34. 13 0
      src/app/contact/contact-detail/contact-detail.page.html
  35. 0 0
      src/app/contact/contact-detail/contact-detail.page.scss
  36. 17 0
      src/app/contact/contact-detail/contact-detail.page.spec.ts
  37. 15 0
      src/app/contact/contact-detail/contact-detail.page.ts
  38. 17 0
      src/app/contact/contact-list/contact-list-routing.module.ts
  39. 20 0
      src/app/contact/contact-list/contact-list.module.ts
  40. 68 0
      src/app/contact/contact-list/contact-list.page.html
  41. 0 0
      src/app/contact/contact-list/contact-list.page.scss
  42. 17 0
      src/app/contact/contact-list/contact-list.page.spec.ts
  43. 73 0
      src/app/contact/contact-list/contact-list.page.ts
  44. 12 0
      src/app/contact/contact-routing.module.ts
  45. 14 0
      src/app/contact/contact.module.ts
  46. 14 0
      src/app/contact/session/session-routing.module.ts
  47. 20 0
      src/app/contact/session/session.module.ts
  48. 37 0
      src/app/contact/session/session.page.html
  49. 69 0
      src/app/contact/session/session.page.scss
  50. 24 0
      src/app/contact/session/session.page.spec.ts
  51. 69 0
      src/app/contact/session/session.page.ts
  52. 8 0
      src/app/contact/user-name.pipe.spec.ts
  53. 19 0
      src/app/contact/user-name.pipe.ts
  54. 17 0
      src/app/lostfound/lostfound-routing.module.ts
  55. 20 0
      src/app/lostfound/lostfound.module.ts
  56. 27 0
      src/app/lostfound/lostfound.page.html
  57. 36 0
      src/app/lostfound/lostfound.page.scss
  58. 17 0
      src/app/lostfound/lostfound.page.spec.ts
  59. 28 0
      src/app/lostfound/lostfound.page.ts
  60. 17 0
      src/app/resort/resort-routing.module.ts
  61. 5 5
      src/app/resort/resort.module.ts
  62. 25 0
      src/app/resort/resort.page.html
  63. 36 0
      src/app/resort/resort.page.scss
  64. 5 5
      src/app/resort/resort.page.spec.ts
  65. 28 0
      src/app/resort/resort.page.ts
  66. 17 0
      src/app/tab3/edit-info/edit-info-routing.module.ts
  67. 20 0
      src/app/tab3/edit-info/edit-info.module.ts
  68. 44 0
      src/app/tab3/edit-info/edit-info.page.html
  69. 0 0
      src/app/tab3/edit-info/edit-info.page.scss
  70. 17 0
      src/app/tab3/edit-info/edit-info.page.spec.ts
  71. 72 0
      src/app/tab3/edit-info/edit-info.page.ts
  72. 26 14
      src/app/tab3/login/login.page.html
  73. 92 14
      src/app/tab3/login/login.page.ts
  74. 17 0
      src/app/tab3/nologin/nologin-routing.module.ts
  75. 5 5
      src/app/tab3/nologin/nologin.module.ts
  76. 13 0
      src/app/tab3/nologin/nologin.page.html
  77. 0 0
      src/app/tab3/nologin/nologin.page.scss
  78. 5 5
      src/app/tab3/nologin/nologin.page.spec.ts
  79. 15 0
      src/app/tab3/nologin/nologin.page.ts
  80. 5 0
      src/app/tab3/tab3-routing.module.ts
  81. 37 70
      src/app/tab3/tab3.page.html
  82. 57 16
      src/app/tab3/tab3.page.ts
  83. 0 16
      src/app/tab4/data.service .spec.ts
  84. 0 121
      src/app/tab4/data.service.ts
  85. 76 0
      src/app/tab4/items.service.ts
  86. 0 22
      src/app/tab4/tab11/tab11-routing.module.ts
  87. 0 62
      src/app/tab4/tab11/tab11.page.html
  88. 0 6
      src/app/tab4/tab11/tab11.page.scss
  89. 0 34
      src/app/tab4/tab11/tab11.page.ts
  90. 0 22
      src/app/tab4/tab12/tab12-routing.module.ts
  91. 0 65
      src/app/tab4/tab12/tab12.page.html
  92. 0 6
      src/app/tab4/tab12/tab12.page.scss
  93. 0 42
      src/app/tab4/tab12/tab12.page.ts
  94. 0 22
      src/app/tab4/tab13/tab13-routing.module.ts
  95. 0 63
      src/app/tab4/tab13/tab13.page.html
  96. 0 6
      src/app/tab4/tab13/tab13.page.scss
  97. 0 42
      src/app/tab4/tab13/tab13.page.ts
  98. 0 22
      src/app/tab4/tab14/tab14-routing.module.ts
  99. 0 62
      src/app/tab4/tab14/tab14.page.html
  100. 0 6
      src/app/tab4/tab14/tab14.page.scss

+ 266 - 0
README.md

@@ -0,0 +1,266 @@
+# 校园互助App产品策划书
+
+# 产品介绍
+- 产品名称:校园帮帮
+- 产品代码:CampusHelp
+- 产品Logan
+    - 校园难题找帮帮,互助友爱响当当!
+- 基础功能
+
+    1. **问题求助**:学生可以在平台上发布学习、生活等方面的问题,如作业难题、课程理解、生活小技巧等,其他同学可以提供解答或帮助。
+    2. **二手买卖**:提供二手书籍、电子产品、生活用品等物品的买卖信息,促进资源的循环利用。
+    3. **失物招领**:发布失物信息,方便同学找回遗失物品,同时也可提供寻物帮助。
+    4. **活动发布**:学生可以发布或查看校园内的各类活动信息,如社团招新、讲座、比赛等,增强校园社交。
+
+制作一款校园互助类app,该app的主要使用成员为校园里生活的同学们,该app主要为同学们互助解决生活,学习等方面的难题。
+# 背景分析
+
+# 一、政策背景
+
+**1. 校园内二手交易市场的发展状况**
+
+近年来,随着环保意识的增强和共享经济理念的普及,校园内二手交易市场逐渐兴起并蓬勃发展。学生群体作为二手商品的主要供需双方,对二手交易的需求日益增长。二手书籍、电子产品、生活用品等成为学生间交易的热门商品。据智研咨询发布的《2020—2026年中国高等学校行业市场现状分析及投资前景预测报告》得知:2019年中国普通本专科招生914.90万人,比2018年增加123.91万人,增长15.67%;在校生3031.53万人,比2018年增加200.49万人,增长7.08%。随着大学生人数的不断增多,各高校近年来快递数不断攀升。
+
+**2. 政策对校园帮帮App发展的影响**
+
+(1)**政策支持**:政府近年来出台了一系列鼓励创新创业、促进数字经济发展的政策,为校园帮帮App等校园服务平台提供了良好的政策环境。特别是针对大学生创新创业的扶持政策,如资金补助、税收优惠等,为校园帮帮App的发展提供了有力支持。
+
+(2)**监管政策**:随着二手交易市场的扩大,政府也加强了对相关市场的监管力度,以保障消费者权益和市场秩序。这对校园帮帮App提出了更高的要求,需要在合法合规的前提下运营,确保交易的真实性和安全性。
+
+# 二、行业背景
+
+
+**1. 市场规模**
+
+校园帮帮App所属的校园服务平台行业市场规模庞大且持续增长。随着移动互联网的普及和大学生消费能力的提升,校园服务市场需求不断增加。据相关数据显示,校园服务市场规模已达到数十亿元,并呈现出快速增长的趋势。
+
+![img,市场数据](/image/641.webp) 
+
+**2. 竞争格局**
+
+当前,校园服务平台行业竞争激烈,众多企业和个人纷纷涌入这一领域。虽然市场上存在一些具有一定规模和影响力的平台,但整体上仍处于竞争初级阶段。校园帮帮App作为其中的一员,需要不断创新和优化服务,以在竞争中脱颖而出。
+
+**3. 发展趋势**
+
+(1)**多元化服务**:未来,校园服务平台将更加注重服务的多元化和个性化,以满足学生群体的多样化需求。除了传统的二手交易、生活服务外,还将拓展到学习辅导、技能交换、文化娱乐等多个领域。
+
+(2)**线上线下融合**:随着技术的发展和消费者需求的变化,线上线下融合将成为校园服务平台的重要发展趋势。通过线上线下相结合的方式,为学生提供更加便捷、高效的服务体验。
+
+(3)**智能化服务**:人工智能、大数据等技术的应用将推动校园服务平台向智能化方向发展。通过算法分析用户行为和需求,为用户提供个性化的服务推荐和解决方案。
+
+# 三、社会背景
+
+**1. 普及程度**
+
+校园帮帮App在校园内具有较高的普及程度。随着移动互联网的普及和大学生对便捷服务的需求增加,越来越多的学生开始使用校园帮帮App等校园服务平台。这些平台为学生提供了便捷的生活和学习解决方案,受到了学生的广泛欢迎。
+
+**2. 用户群体**
+
+校园帮帮App的主要用户群体为在校大学生和研究生等学生群体。他们具有较高的消费能力和强烈的社交需求,对便捷、高效的服务有着较高的要求。同时,学生群体之间的互助精神也为校园帮帮App的发展提供了良好的社会基础。
+
+**3. 市场潜力**
+
+校园帮帮App的市场潜力巨大。随着学生群体规模的扩大和需求的多样化发展,校园服务平台市场将持续增长。同时,随着技术的不断进步和消费者需求的变化,校园帮帮App等校园服务平台将不断拓展新的服务领域和商业模式,实现更加广阔的发展前景。
+
+# 四、建议和改进措施
+
+**1. 加强品牌建设**
+
+校园帮帮App应加强品牌建设,提高品牌知名度和美誉度。通过线上线下相结合的方式进行宣传推广活动,吸引更多用户关注和使用。同时,注重用户体验和服务质量提升,树立良好的品牌形象。
+
+**2. 拓展服务领域**
+
+校园帮帮App应不断拓展服务领域和商业模式创新。根据用户需求和市场变化及时调整服务内容和方式,提供更加多元化、个性化的服务解决方案。同时积极探索新的盈利模式和市场机会实现可持续发展。
+
+**3. 加强技术研发**
+
+校园帮帮App应加大技术研发投入力度提高技术水平和创新能力。通过引入先进的人工智能、大数据等技术手段优化用户体验和服务效率提升平台竞争力。同时加强安全防护和隐私保护确保用户信息和交易安全。
+
+**4. 深化校园合作**
+
+校园帮帮App应积极深化与高校的合作关系共同推动校园服务平台的发展。通过与高校建立紧密的合作关系可以获得更多的资源和支持促进平台的快速发展。同时加强与高校师生的沟通交流了解他们的需求和反馈不断优化服务内容和方式。
+
+
+# 需求分析
+## 用户分析
+
+校园帮帮App的基础功能设计得十分贴合学生群体的实际需求,因此它面向的对象非常广泛且具体,主要包括以下几类:
+
+1. 全体学生:无论是新生还是老生,所有学生都可能在学习、生活中遇到需要求助的情况,或者想要出售二手物品、寻找失物、参与校园活动。因此,全体学生都是校园帮帮的直接受益者和使用者。
+
+2. 学习困难者:对于某些课程理解有难度或作业遇到难题的学生,问题求助功能提供了向同学求助的便捷途径,帮助他们及时解决问题,提高学习效率。
+
+3. 二手物品交易者:想要出售不再需要的书籍、电子产品、生活用品等物品的学生,以及希望以更实惠的价格购买这些物品的学生,都会频繁使用二手买卖功能。
+
+4. 失物者与拾得者:不小心遗失物品的学生可以通过失物招领功能发布寻物启事,而捡到他人物品的学生则可以通过该功能快速找到失主,实现物品的归还。
+
+5. 社团组织者:社团招新、举办讲座、比赛等活动是校园生活的重要组成部分,社团组织者可以通过活动发布功能宣传自己的活动,吸引更多参与者,增强社团影响力。
+
+校园帮帮App面向的对象是全体在校学生,特别是那些在学习、生活、社交等方面有实际需求的学生群体。通过针对这些具有代表性的用户群体的需求和特点,可以更好地定位产品的市场定位和推广策略,吸引更多潜在用户体验和使用校园帮帮。
+## 痛点分析
+1. 全体学生
+    - 痛点需求:信息获取不畅、社交需求未满足、生活便利性不足。
+    - 共性需求:希望高效获取校园内外的各类信息。便捷地参与校园活动,丰富课余生活。建立一个互助共享的学习环境。
+    - 差异化需求:新生可能更需要入学指导、课程推荐等适应新环境的帮助。毕业生则更关注就业信息、招聘会通知等。
+2. 学习困难者
+    - 痛点需求:学习资源有限、学习动力不足。
+    - 共性需求:希望快速获得学习上的帮助和解答,与同学建立学习小组,共同进步。
+    - 差异化需求:不同学科的学习困难者需要针对性的学习资源和辅导。有些同学可能更需要时间管理和学习方法的指导。
+3. 二手物品交易者
+    - 痛点需求:交易信息不对称、交易过程繁琐、存在风险。
+    - 共性需求:希望建立安全、便捷的二手交易平台,有清晰的商品信息和价格比较。
+    - 差异化需求:卖家可能希望平台能提供更多的曝光机会。买家则可能更注重商品的品质和售后服务。
+4. 失物者与拾得者
+    - 痛点需求:失物找回难度大、信息沟通不畅,可能导致失物长时间无法归还。
+    - 共性需求:一个集中发布和查询失物信息的平台。简便快捷的失物认领流程。
+    - 差异化需求:某些特殊物品(如证件、电子设备)的失主可能更急需找回。拾得者可能希望平台能提供一定的奖励或激励机制。
+5. 社团组织者
+    - 痛点需求:活动宣传受限,由于信息不对称,很多学生对社团活动不了解或不感兴趣。
+    - 共性需求:高效、低成本的宣传渠道。提高活动的知名度和参与度。
+    - 差异化需求:不同类型的社团(如学术类、文艺类、体育类等)可能需要不同形式的宣传和推广策略。大型活动可能需要平台提供更多的技术支持和资源投入。
+
+通过深度分析不同用户群体的痛点需求、共性需求和差异化需求,可以更有针对性地设计产品功能和服务,满足用户的多样化需求,提升产品的用户体验和市场竞争力。
+
+# 功能分析
+针对“校园帮帮”App,为了充分体现其商业价值和社会价值,以下是一些建议的核心功能模块策划:
+
+1. 问题求助与解答
+    - 功能描述:学生可以在此模块发布学习、生活等方面的问题,其他用户可以提供解答或帮助,设有积分奖励机制鼓励解答者。
+    - 商业价值:通过增加用户粘性和活跃度,平台带来稳定的用户基础,未来可引入广告、付费提问、专家解答等增值服务。
+    - 社会价值:促进学习资源共享,提高学习效率,增强学生间的互助精神。
+2. 二手交易市场
+    - 功能描述:提供二手书籍、电子产品、生活用品等物品的买卖信息发布和交易功能。支持商品详情展示、在线议价、安全交易保障(如实名认证等)。
+    - 商业价值:通过交易佣金、广告位出租、会员服务等方式实现盈利。
+    - 社会价值:促进资源的循环利用,减少浪费,培养学生的环保意识。
+3. 失物招领与寻物启事
+    - 功能描述:用户可以发布失物信息和寻物启事,通过平台快速匹配失主和拾得者。
+    - 商业价值:虽然直接商业价值较低,但可增强用户粘性,提升平台口碑,间接促进其他功能模块的使用。
+    - 社会价值:帮助失主找回重要物品,减少因失物带来的不便和损失,传递正能量。
+4. 活动发布
+    - 功能描述:社团、组织或个人可以在此发布校园内的各类活动信息(如讲座、比赛、社团招新等),用户可查看信息。
+    - 商业价值:通过活动赞助、广告植入、票务销售等方式实现盈利。
+    - 社会价值:丰富校园文化生活,促进学生之间的交流与互动,增强校园凝聚力。
+
+校园帮帮通过这些核心功能模块,不仅能够实现商业价值的最大化,还能在促进学生成长、资源循环利用、校园文化建设等方面发挥重要的社会价值。
+# 竞品分析
+## 竞品的搜索和整理
+- 参考校园互助的内容类别 https://kns.cnki.net/reader/flowpdf?invoice=wZeYIXHUwzOGR3JSjzb5qlziQ9JQeIp8vGjyGJ%2F8U5C7wYj2yOUOHnO4W6vqwM85ieEMKBYCRkoT9JOSpoHK8YCDGuc%2FeK6Y9ElUwbFUi1IXp9HRJaf2m5hGKKunVpGg0MXRmhpSJDPvfv07NRxjOyoa7wIDhDVHPpy2O1wHqBY%3D&platform=NZKPT&product=CMFD&filename=1017856732.nh&tablename=cmfd201802&type=DISSERTATION&scope=trial&cflag=pdf&dflag=&pages=&language=chs&trial=&nonce=813F9DD0008243D59F25E396D5F4AF1B
+    - 脸帮
+    - 校园帮棒
+- 参考界面UI设计
+    - 知乎 https://www.zhihu.com/
+    - 微博 https://weibo.com/newlogin?tabtype=weibo&gid=102803&openLoginLayer=0&url=https%3A%2F%2Fweibo.com%2F
+    - 贴吧 https://tieba.baidu.com/index.html
+    - 小红书 https://www.xiaohongshu.com/explore
+- 参考数据 
+    - https://kns.cnki.net/kns8s/defaultresult/index?crossids=YSTT4HG0%2CLSTPFY1C%2CJUP3MUPD%2CMPMFIG1A%2CWQ0UVIAA%2CBLZOG7CK%2CEMRPGLPA%2CPWFIRAGL%2CNLBO1Z6R%2CNN3FJMUV&korder=SU&kw=%E6%A0%A1%E5%9B%AD%E4%BA%92%E5%8A%A9
+
+## 竞品功能拆解各维度对比
+- 核心功能:发单、接单
+    - 用户操作逻辑
+    - UI界面布局
+    - 产品结构及页面结构分析
+
+![img,校园帮棒分析](/image/校园帮棒分析.png) 
+
+# 核心功能的产品开发计划
+## 确认核心功能
+- 首页
+- 消息
+- 发布
+- 我的
+# 产品结构梳理图
+## 首页的结构产品结构
+- 首页
+    - 顶部导航
+        - 求助
+        - 二手交易
+        - 失物招领
+        - 活动
+    -   功能区域
+        - 查看已发布信息
+        - 回复、发帖
+- 消息
+    - 联系人
+        -对话框
+- 发布
+    - 顶部Tabs
+        - 求助
+            - 问题
+        - 二手交易
+            - 产品
+            - 交易金额
+            - 交易时间
+            - 交易地点
+            - 备注
+        - 失物招领
+            - 物品
+            - 丢失/拾取
+            - 地点
+            - 大致时间
+            - 物品图片
+            - 备注
+        - 活动
+            - 活动名称
+            - 可参与对象
+            - 开始时间
+            - 持续时长
+            - 地点
+            - 备注
+
+## 信息结构图梳理
+- 发布内容信息
+    - 标题
+    - 发布者
+    - 标签
+    - 内容
+    - 发布人地址
+- 标签信息
+    - 发布内容标签
+    - 主题标签
+- 用户信息
+    - 用户id
+    - 校区楼栋
+    - 头像
+    - 学号
+- 二手商品信息
+    - 价格
+    - 成色
+    - 已卖出
+    - 为卖出
+- 评论信息
+    - 评论内容
+    - 用户名
+- 聊天信息
+    - 已读
+    - 未读
+# 各页面对标竞品截图
+1. 脸帮
+
+![img,脸帮](/image/脸帮.png)
+
+2. 校园帮棒
+
+![img,校园帮棒](/image/校园帮棒.png)
+
+## 各页面对标竞品截图梳理
+- 首页对标
+<style>
+    img1{
+        height:300px;
+        wight:200px
+    }
+</style>
+![img1,首页](/image/首页.jpg) 
+<!-- <img src="./image/首页.jpg"> -->
+- 消息页面对标
+
+![img1,消息](/image/消息.jpg) 
+
+- 发布页面对标
+
+![img1,发布](/image/发布.jpg) 
+
+- 我的页面对标
+
+![img1,我的](/image/我的.jpg) 

二进制
image/641.webp


二进制
image/发布.jpg


二进制
image/我的.jpg


二进制
image/校园帮棒.png


二进制
image/校园帮棒分析.png


二进制
image/消息.jpg


二进制
image/脸帮.png


二进制
image/首页.jpg


+ 0 - 0
src/app/server/REST.js → server/REST.js


+ 42 - 0
server/case/promise.js

@@ -0,0 +1,42 @@
+
+async function main(){
+    // setTimeout(() => {
+    //     console.log(1)
+    // }, 500);
+    // setTimeout(() => {
+    //     console.log(2)
+    // }, 200);
+    // setTimeout(() => {
+    //     console.log(3)
+    // }, 100);
+    // setTimeout(() => {
+    //     console.log(4)
+    // }, 1000);
+    // return
+
+    // waitSeconds(500,()=>{console.log(1)}) // 500
+    // waitSeconds(200,()=>{console.log(2)}) // 700
+    // waitSeconds(100,()=>{console.log(3)}) // 800
+    // waitSeconds(1000,()=>{console.log(4)}) // 1800
+    // return
+    
+    let res1 = await waitSeconds(500,()=>{console.log(1)}) // 500
+    console.log(res1)
+    let res2 = await waitSeconds(200,()=>{console.log(2)}) // 700
+    console.log(res2)
+    let res3 = await waitSeconds(100,()=>{console.log(3)}) // 800
+    console.log(res3)
+    let res4 = await waitSeconds(1000,()=>{console.log(4)}) // 1800
+    console.log(res4)
+}
+
+function waitSeconds(duration,handle) {
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            handle()
+            resolve(`等待了${duration}ms`);
+        }, duration);
+    });
+}
+
+main()

+ 110 - 0
server/fmode-parse copy.js

@@ -0,0 +1,110 @@
+class ParseObject{
+    className
+    id
+    serverURL = "http://web2023.fmode.cn:9999/parse"
+    data = {}
+    toJSON(){
+        return this.data
+    }
+    constructor(className){
+        this.className = className
+    }
+    async get(id){
+        let response = await fetch(this.serverURL+"/classes/"+this.className+"/"+id, {
+            "headers": {
+                "x-parse-application-id": "dev"
+              },
+            "body": null,
+            "method": "GET",
+            "mode": "cors",
+            "credentials": "omit"
+          });
+          if(response?.status=="200"){
+            let json = await response.json()
+            this.id = json?.objectId;
+            delete json.objectId
+            delete json.createdAt
+            delete json.updatedAt
+            this.data = json;
+            return this
+          }else{
+            return []
+          }
+    }
+    async findAll(){
+        let response = await fetch(this.serverURL+"/classes/"+this.className, {
+            "headers": {
+                "x-parse-application-id": "dev"
+              },
+            "body": null,
+            "method": "GET",
+            "mode": "cors",
+            "credentials": "omit"
+          });
+          console.log(response)
+          return []
+          if(response?.status=="200"){
+            let json = await response.json()
+            // console.log(json)
+            return json?.results || []
+          }else{
+            return []
+          }
+    }
+    set(data){
+        this.data = data
+    }
+    async save(){
+        let body = JSON.stringify(this.data)
+        let url = this.serverURL+"/classes/"+this.className // 创建URL
+        let method = "POST"
+        if(this.id){
+            url = url + "/" + this.id // 更新URL
+            method = "PUT"
+        }
+        console.log(url,method,body)
+        let response = await fetch(url, {
+            "headers": {
+                // "content-type": "text/plain;charset=UTF-8",
+                "x-parse-application-id": "dev"
+            },
+            "body": body,
+            "method": method,
+            "mode": "cors",
+            "credentials": "omit"
+        });
+        console.log(body)
+        let text = await response?.text();
+        console.log(text)
+        let json = await response?.json();
+        if(json?.objectId){
+            console.log(json)
+            this.id = json?.objectId
+            return this
+        }else{
+            return null
+        }
+    }
+    async delete(){
+        let response = await fetch(this.serverURL+"/classes/"+this.className+"/"+id, {
+            "headers": {
+                "x-parse-application-id": "dev"
+              },
+            "body": null,
+            "method": "DELETE",
+            "mode": "cors",
+            "credentials": "omit"
+          });
+          if(response?.status=="200"){
+            let json = await response.json()
+            return json
+          }else{
+            return []
+          }
+    }
+}
+module.exports.ParseObject = ParseObject
+
+class ParseQuery{
+    
+}

+ 40 - 0
server/fmode-parse-test.js

@@ -0,0 +1,40 @@
+const { ParseObject } = require("./fmode-parse");
+
+async function main(){
+    // testFind()
+    // testSave()
+    testUpdate()
+}
+
+async function testUpdate(){
+    // 根据ID获取
+    let lostfound = new ParseObject("Items");
+    lostfound = await lostfound.get("w03dUQKOJG")
+    console.log(lostfound)
+    // 根据有ID的对象,保存新属性
+    lostfound.set({
+        name:"学生证"
+    })
+    lostfound.save()
+
+}
+async function testFind(){
+        // 查询测试
+        let Items = new ParseObject("Items")
+        let list = await Items.findAll()
+        console.log(list)
+    
+}
+async function testSave(){
+       // 创建测试
+       let lostfound = new ParseObject("Items")
+       lostfound.set({
+        category: '失物招领',
+        title: '李小珊洗衣液',
+        publisher: '小耶',
+       })
+       lostfound = await lostfound.save();
+       console.log(lostfound) // id w03dUQKOJG
+       console.log(lostfound.toJSON())
+}
+main()

+ 110 - 0
server/fmode-parse.js

@@ -0,0 +1,110 @@
+class ParseObject{
+    className
+    id
+    serverURL = "http://web2023.fmode.cn:9999/parse"
+    data = {}
+    toJSON(){
+        return this.data
+    }
+    constructor(className){
+        this.className = className
+    }
+    async get(id){
+        let response = await fetch(this.serverURL+"/classes/"+this.className+"/"+id, {
+            "headers": {
+                "x-parse-application-id": "dev"
+              },
+            "body": null,
+            "method": "GET",
+            "mode": "cors",
+            "credentials": "omit"
+          });
+          if(response?.status=="200"){
+            let json = await response.json()
+            this.id = json?.objectId;
+            delete json.objectId
+            delete json.createdAt
+            delete json.updatedAt
+            this.data = json;
+            return this
+          }else{
+            return []
+          }
+    }
+    async findAll(){
+        let response = await fetch(this.serverURL+"/classes/"+this.className, {
+            "headers": {
+                "x-parse-application-id": "dev"
+              },
+            "body": null,
+            "method": "GET",
+            "mode": "cors",
+            "credentials": "omit"
+          });
+          // console.log(response)
+          // return []
+          if(response?.status=="200"){
+            let json = await response.json()
+            // console.log(json)
+            return json?.results || []
+          }else{
+            return []
+          }
+    }
+    set(data){
+        this.data = data
+    }
+    async save(){
+        let body = JSON.stringify(this.data)
+        let url = this.serverURL+"/classes/"+this.className // 创建URL
+        let method = "POST"
+        if(this.id){
+            url = url + "/" + this.id // 更新URL
+            method = "PUT"
+        }
+        console.log(url,method,body)
+        let response = await fetch(url, {
+            "headers": {
+                // "content-type": "text/plain;charset=UTF-8",
+                "x-parse-application-id": "dev"
+            },
+            "body": body,
+            "method": method,
+            "mode": "cors",
+            "credentials": "omit"
+        });
+        console.log(body)
+        let text = await response?.text();
+        console.log(text)
+        let json = await response?.json();
+        if(json?.objectId){
+            console.log(json)
+            this.id = json?.objectId
+            return this
+        }else{
+            return null
+        }
+    }
+    async delete(){
+        let response = await fetch(this.serverURL+"/classes/"+this.className+"/"+id, {
+            "headers": {
+                "x-parse-application-id": "dev"
+              },
+            "body": null,
+            "method": "DELETE",
+            "mode": "cors",
+            "credentials": "omit"
+          });
+          if(response?.status=="200"){
+            let json = await response.json()
+            return json
+          }else{
+            return []
+          }
+    }
+}
+module.exports.ParseObject = ParseObject
+
+class ParseQuery{
+    
+}

+ 112 - 0
server/rest copy.js

@@ -0,0 +1,112 @@
+
+//Items GET 获取全部内容
+async function getPet(){
+  let response = await fetch("http://web2023.fmode.cn:9999/parse/classes/Items?", {
+    "headers": {
+      "accept": "*/*",
+      "accept-language": "zh-CN,zh;q=0.9",
+      "if-none-match": "W/\"7e0-mc6U9HFyuJHJIduZnf+lM36E+qs\"",
+      "x-parse-application-id": "dev"
+    },
+    "referrer": "http://127.0.0.1:4040/",
+    "referrerPolicy": "strict-origin-when-cross-origin",
+    "body": null,
+    "method": "GET",
+    "mode": "cors",
+    "credentials": "omit"
+  });
+  // console.log(response)
+  // return []
+  if(response?.status=="200"){
+    let json = await response.json()
+    // console.log(json)
+    return json?.results || []
+  }else{
+    return []
+  }
+}
+
+async function main(){
+  // let petList = await getPet()
+  // console.log(petList)
+  // let list = await ClassesGet("Pet")
+  // console.log(list)
+
+  let list = await ClassesGet("Items")
+  console.log(list)
+}
+main()
+
+// 通过函数封装,实现多个表的查询
+async function ClassesGet(className){
+  let response = await fetch("http://web2023.fmode.cn:9999/parse/classes/"+className, {
+    "headers": {
+      "accept": "*/*",
+      "accept-language": "zh-CN,zh;q=0.9",
+      "if-none-match": "W/\"7e0-mc6U9HFyuJHJIduZnf+lM36E+qs\"",
+      "x-parse-application-id": "dev"
+    },
+    "referrer": "http://127.0.0.1:4040/",
+    "referrerPolicy": "strict-origin-when-cross-origin",
+    "body": null,
+    "method": "GET",
+    "mode": "cors",
+    "credentials": "omit"
+  });
+  // console.log(response)
+  // return []
+  if(response?.status=="200"){
+    let json = await response.json()
+    // console.log(json)
+    return json?.results || []
+  }else{
+    return []
+  }
+}
+
+// //Items Post 创建数据
+// fetch("http://web2023.fmode.cn:9999/parse/classes/Items", {
+//   "headers": {
+//     "accept": "*/*",
+//     "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
+//     "content-type": "text/plain;charset=UTF-8",
+//     "x-parse-application-id": "dev"
+//   },
+//   "referrer": "http://127.0.0.1:4040/",
+//   "referrerPolicy": "strict-origin-when-cross-origin",
+//   "body": "{\"category\": \"二手交易\",\"title\": \"ipad9二手出售\",\"publisher\": \"耶耶\", \n   \"headImage\":\"/assets/images/black.png\",\"name\":\"ipad9\",\"amount\":800\n    }",
+//   "method": "POST",
+//   "mode": "cors",
+//   "credentials": "omit"
+// });
+
+// //Items Put 添加数据
+// fetch("http://web2023.fmode.cn:9999/parse/classes/Items/l3XZKsAIPO", {
+//   "headers": {
+//     "accept": "*/*",
+//     "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
+//     "content-type": "text/plain;charset=UTF-8",
+//     "x-parse-application-id": "dev"
+//   },
+//   "referrer": "http://127.0.0.1:4040/",
+//   "referrerPolicy": "strict-origin-when-cross-origin",
+//   "body": "{\"location\":\"大学生活动中心\",\"image\":\"/assets/images/ipad9.jpg\",\"content\":\"不可议价\",\"repliesId\":\"6\"}",
+//   "method": "PUT",
+//   "mode": "cors",
+//   "credentials": "omit"
+// });
+
+// //Items Detele 删除数据
+// fetch("http://web2023.fmode.cn:9999/parse/classes/Items/l3XZKsAIPO", {
+//   "headers": {
+//     "accept": "*/*",
+//     "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
+//     "x-parse-application-id": "dev"
+//   },
+//   "referrer": "http://127.0.0.1:4040/",
+//   "referrerPolicy": "strict-origin-when-cross-origin",
+//   "body": null,
+//   "method": "DELETE",
+//   "mode": "cors",
+//   "credentials": "omit"
+// });

+ 17 - 0
src/app/activity/activity-routing.module.ts

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

+ 20 - 0
src/app/activity/activity.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 { ActivityPageRoutingModule } from './activity-routing.module';
+
+import { ActivityPage } from './activity.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    ActivityPageRoutingModule
+  ],
+  declarations: [ActivityPage]
+})
+export class ActivityPageModule {}

+ 24 - 0
src/app/activity/activity.page.html

@@ -0,0 +1,24 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <div class="item-container">
+      <ion-avatar class="avatar-container">
+        <ion-img [src]="items?.get('headImage')" alt="头像"></ion-img>
+      </ion-avatar>
+      <div class="info-container">
+        <p class="publisher">{{items?.get("publisher")}}</p>
+        <h2 class="title">{{items?.get("title")}}</h2>
+      </div>
+    </div>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <ion-card class="content">
+    <p>活动名:{{items?.get("name")}}</p>
+    <p>可参与对象:{{items?.get("target")}}</p>
+    <p>开始时间:{{items?.get("time")}}</p>
+    <p>时长:{{items?.get("duration")}}</p>
+    <P>地点:{{items?.get("location")}}</P>
+    <p>内容:{{items?.get("content")}}</p>
+  </ion-card>
+</ion-content>

+ 36 - 0
src/app/activity/activity.page.scss

@@ -0,0 +1,36 @@
+.item-container {
+    display: flex;
+    align-items: center;
+}
+  
+.avatar-container {
+    margin-left: 10px;
+}
+  
+.info-container {
+    margin-left: 10px;
+}
+
+.title {
+    color: #000000; /* 设置字体颜色为黑色 */
+    font-size: 1.2em; /* 设置字体大小为1.2em */
+}
+
+.content {
+    color: #000000; 
+    font-size: 1.0em; 
+}
+
+.image-container {
+    width: 200px; /* 设置图片容器的宽度 */
+    height: 200px; /* 设置图片容器的高度 */
+    display: flex;
+}
+
+.image-container ion-img {
+    margin-left: 30px;
+    // margin-bottom: 10px;
+    margin-top:-10px;
+    width: 100%;
+    height: 100%;
+}

+ 5 - 5
src/app/tab4/tab13/tab13.page.spec.ts → src/app/activity/activity.page.spec.ts

@@ -1,12 +1,12 @@
 import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { Tab13Page } from './tab13.page';
+import { ActivityPage } from './activity.page';
 
-describe('Tab13Page', () => {
-  let component: Tab13Page;
-  let fixture: ComponentFixture<Tab13Page>;
+describe('ActivityPage', () => {
+  let component: ActivityPage;
+  let fixture: ComponentFixture<ActivityPage>;
 
   beforeEach(() => {
-    fixture = TestBed.createComponent(Tab13Page);
+    fixture = TestBed.createComponent(ActivityPage);
     component = fixture.componentInstance;
     fixture.detectChanges();
   });

+ 28 - 0
src/app/activity/activity.page.ts

@@ -0,0 +1,28 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import Parse from "parse";
+
+@Component({
+  selector: 'app-activity',
+  templateUrl: './activity.page.html',
+  styleUrls: ['./activity.page.scss'],
+})
+export class ActivityPage implements OnInit {
+
+  constructor(private route:ActivatedRoute) { }
+
+  ngOnInit() {
+    this.loadItemsById()
+  }
+
+  items:Parse.Object|undefined
+  async loadItemsById() {
+    let id = this.route.snapshot.params["id"]
+
+    if(id){
+      let query = new Parse.Query("Items");
+      this.items = await query.get(id);
+    }
+  }
+
+}

+ 20 - 8
src/app/app-routing.module.ts

@@ -11,20 +11,24 @@ const routes: Routes = [
     loadChildren: () => import('./tab4/tab4.module').then( m => m.Tab4PageModule)
   },
   {
-    path: 'tab11',
-    loadChildren: () => import('./tab4/tab11/tab11.module').then( m => m.Tab11PageModule)
+        path: 'tab3',
+        loadChildren: () => import('./tab3/tab3.module').then(m => m.Tab3PageModule)
   },
   {
-    path: 'tab12',
-    loadChildren: () => import('./tab4/tab12/tab12.module').then( m => m.Tab12PageModule)
+    path: 'trade',
+    loadChildren: () => import('./trade/trade.module').then( m => m.TradePageModule)
   },
   {
-    path: 'tab13',
-    loadChildren: () => import('./tab4/tab13/tab13.module').then( m => m.Tab13PageModule)
+    path: 'resort',
+    loadChildren: () => import('./resort/resort.module').then( m => m.ResortPageModule)
   },
   {
-    path: 'tab14',
-    loadChildren: () => import('./tab4/tab14/tab14.module').then( m => m.Tab14PageModule)
+    path: 'lostfound',
+    loadChildren: () => import('./lostfound/lostfound.module').then( m => m.LostfoundPageModule)
+  },
+  {
+    path: 'activity',
+    loadChildren: () => import('./activity/activity.module').then( m => m.ActivityPageModule)
   },
   {
     path: 'chat',
@@ -37,6 +41,14 @@ const routes: Routes = [
   {
     path: 'login',
     loadChildren: () => import('./tab3/login/login.module').then( m => m.LoginPageModule)
+  },
+  {
+    path: 'nologin',
+    loadChildren: () => import('./tab3/nologin/nologin.module').then( m => m.NologinPageModule)
+  },
+  {
+    path: 'edit-info',
+    loadChildren: () => import('./tab3/edit-info/edit-info.module').then( m => m.EditInfoPageModule)
   }
 ];
 @NgModule({

+ 2 - 1
src/app/app.component.ts

@@ -1,7 +1,8 @@
 import { Component } from '@angular/core';
 import * as Parse from "parse";
 Parse.initialize("dev");
-(Parse as any).serverURL = 'http://web2023.fmode.cn:9999/parse'
+(Parse as any).serverURL = 'http://web2023.fmode.cn:9999/parse';
+(Parse as any).liveQueryServerURL = 'http://web2023.fmode.cn:9999/parse';
 
 @Component({
   selector: 'app-root',

+ 1 - 0
src/app/app.module.ts

@@ -12,5 +12,6 @@ import { AppComponent } from './app.component';
   imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
   providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
   bootstrap: [AppComponent],
+  
 })
 export class AppModule {}

+ 79 - 0
src/app/contact/README.md

@@ -0,0 +1,79 @@
+# 通讯模块
+
+# 项目结构
+- chat 对话页面
+- session 历史会话
+- contact-list 通讯录列表
+
+
+# 对话模块
+- 功能简介:两个用户互相发消息,接收消息,查看消息历史记录
+
+## 数据范式
+- _User 用户表
+    - objectId
+    - username 用户名
+    - mobile 手机号
+    - nickname 昵称
+- Contact 通讯好友的表
+    - from Pointer<_User>
+    - to Pointer<_User>
+- Message 消息表
+    - sendUser Pointer<_User>
+    - receiveUser Pointer<_User>
+    - contentJson 消息对象 符合各类消息格式
+    - isRead Boolean 消息已读
+    - isCancel Boolean 消息撤回
+
+思考:图片链接和图片消息的区别,和表达方式?
+图片链接,本质上是文本消息(基于字符串)
+图片消息,本质上是结构化消息 {type:"image",imgUrl:"https://file-cloud.fmode.cn/E4KpGvTEto/20230822/3mkf41033623275.png"}
+
+### 消息内容规范(参考GPT多模态响应)
+- 参考文档:https://ai.fmode.cn/chat/share/nxZMG3CZrd
+
+普通文本消息:
+
+{
+      "role": "user",
+      "content": "Here is the text: 'The quick brown fox jumps over the lazy dog.'"
+}
+
+ 图片消息
+{
+    "role": "user",
+    "content": {
+    "type": "image",
+    "data": {
+        "url": "https://example.com/image.jpg",
+        "alt_text": "A quick brown fox jumping over a lazy dog."
+    }
+    }
+}
+
+音频消息
+
+{
+    "role": "user",
+    "content": {
+    "type": "audio",
+    "data": {
+        "url": "https://example.com/audio.mp3",
+        "alt_text": "An audio clip of a quick brown fox jumping over a lazy dog."
+    }
+    }
+}
+
+### 业务逻辑
+
+#### 发送消息
+- 进入会话页面
+    - 无记录:输入用户昵称添加好友,再进行对话
+    - 有记录:点击历史会话进入
+- 向微服务创建一个Message,sendUser为自身,receiveUser为联系人
+
+#### 接收消息
+- 查询,receiveUser为自身,sendUser为会话窗口联系人的所有Message
+
+#### 会话列表
+- 查询,receiveUser为自身,所有Message,并每个receiver只显示最新一条

+ 13 - 0
src/app/contact/chat/chat-routing.module.ts

@@ -0,0 +1,13 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { ChatPage } from './chat.page';
+// import { Chat1Page } from './tab1 copy/tab11.page';
+const routes: Routes = [
+  { path: '', component: ChatPage },
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class ChatPageRoutingModule {}

+ 7 - 9
src/app/tab4/tab13/tab13.module.ts → src/app/contact/chat/chat.module.ts

@@ -1,20 +1,18 @@
+import { IonicModule } from '@ionic/angular';
 import { NgModule } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
+import { ChatPage } from './chat.page';
 
-import { IonicModule } from '@ionic/angular';
-
-import { Tab13PageRoutingModule } from './tab13-routing.module';
-
-import { Tab13Page } from './tab13.page';
+import { ChatPageRoutingModule } from './chat-routing.module';
 
 @NgModule({
   imports: [
+    IonicModule,
     CommonModule,
     FormsModule,
-    IonicModule,
-    Tab13PageRoutingModule
+    ChatPageRoutingModule
   ],
-  declarations: [Tab13Page]
+  declarations: [ChatPage]
 })
-export class Tab13PageModule {}
+export class ChatPageModule {}

+ 66 - 0
src/app/contact/chat/chat.page.html

@@ -0,0 +1,66 @@
+<ion-header>
+  <ion-toolbar color="primary">
+    <ion-buttons slot="start">
+      <ion-back-button defaultHref="/tabs/session"></ion-back-button>
+    </ion-buttons>
+    <ion-title>{{ contact?.get('name') || contact?.get('to')?.get("nickname") ||contact?.get('to')?.get('username') }}</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content class="chat-content">
+  <ion-list lines="none">
+    <ion-item *ngFor="let message of messageList" class="message-item">
+      <ion-label class="message-container"
+                 [class.sent-message-container]="message?.get('sendUser')?.id === user?.id"
+                 [class.received-message-container]="message?.get('receiveUser')?.id === targetId">
+        <div class="message-details">
+          <p [class.sent-message]="message?.get('sendUser')?.id === user?.id"
+             [class.received-message]="message?.get('receiveUser')?.id === targetId"
+             class="message-text">{{ message?.get("contentJson")?.content }}</p>
+          <div class="message-time">
+            <p>{{ message.createdAt|date:'HH:mm:ss' }}</p>
+          </div>
+        </div>
+      </ion-label>
+      <ion-avatar slot="end" *ngIf="message?.get('sendUser')?.id === user?.id" class="message-avatar sent-avatar">
+        <img [src]="'/assets/img/clock.png'">
+      </ion-avatar>
+      <ion-avatar slot="start" *ngIf="message?.get('receiveUser')?.id === targetId" class="message-avatar received-avatar">
+        <img [src]="'/assets/img/clock.png'">
+      </ion-avatar>
+    </ion-item>
+  </ion-list>
+
+  <ion-list lines="none">
+    <ion-item *ngFor="let message of messages" class="message-item">
+      <ion-label class="message-container"
+                 [class.sent-message-container]="message.type === 'sent'"
+                 [class.received-message-container]="message.type === 'received'">
+        <div class="message-details">
+          <p [class.sent-message]="message.type === 'sent'"
+             [class.received-message]="message.type === 'received'"
+             class="message-text">{{ message.text }}</p>
+          <div class="message-time">
+            <p>{{ message.time }}</p>
+          </div>
+        </div>
+      </ion-label>
+      <ion-avatar slot="end" *ngIf="message.type === 'sent'" class="message-avatar sent-avatar">
+        <img [src]="message.avatar">
+      </ion-avatar>
+      <ion-avatar slot="start" *ngIf="message.type === 'received'" class="message-avatar received-avatar">
+        <img [src]="message.avatar">
+      </ion-avatar>
+    </ion-item>
+  </ion-list>
+</ion-content>
+
+<ion-footer>
+  <ion-toolbar>
+    <ion-input placeholder="Type your message..." [(ngModel)]="newMessage" clearInput></ion-input>
+    <ion-buttons slot="end">
+      <ion-button (click)="sendMessage()" color="primary" [disabled]="!newMessage">Send</ion-button>
+    </ion-buttons>
+  </ion-toolbar>
+</ion-footer>
+

+ 79 - 0
src/app/contact/chat/chat.page.scss

@@ -0,0 +1,79 @@
+.chat-content {
+  padding: 10px;
+}
+
+ion-item.message-item {
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+  margin-bottom: 10px;
+  max-width: 80%; /* 控制消息框的最大宽度 */
+}
+
+.message-container {
+  padding: 8px 12px;
+  display: inline-block;
+  max-width: 80%; /* 控制消息内容的最大宽度 */
+}
+
+.sent {
+  justify-content: flex-end; /* 将发送的消息右对齐 */
+}
+
+.received {
+  justify-content: flex-start; /* 将接收的消息左对齐 */
+}
+
+.sent-message-container {
+  background-color: #dcf8c6; /* 发送消息的背景颜色 */
+  align-self: flex-end;
+  border-radius: 10px;
+  padding: 10px;
+  margin-bottom: 4px;
+  max-width: 70%; /* 适当调整最大宽度 */
+  text-align: right; /* 文本右对齐 */
+}
+
+.received-message-container {
+  background-color: #f0f0f0; /* 接收消息的背景颜色 */
+  align-self: flex-start;
+  border-radius: 10px;
+  padding: 10px;
+  margin-bottom: 4px;
+  max-width: 70%; /* 适当调整最大宽度 */
+  text-align: left; /* 文本左对齐 */
+}
+.sent-message-container[_ngcontent-ng-c914708361]{
+  max-width: 100%;
+}
+ion-avatar[_ngcontent-ng-c914708361]{
+  margin-top:28px;
+}
+.list-md{
+  width: 123%;
+}
+.received-message-container[_ngcontent-ng-c914708361]{
+  max-width: 100%;
+}
+
+.sent-message, .received-message {
+  color: #333; /* 消息文本颜色 */
+}
+
+.message-time {
+  font-size: 12px;
+  color: #888; /* 时间文本颜色 */
+  margin-top: 4px;
+}
+
+.message-details {
+  display: flex;
+  flex-direction: column;
+}
+
+ion-avatar {
+  width: 32px;
+  height: 32px;
+  margin-left: 8px;
+  margin-right: 8px;
+}

+ 26 - 0
src/app/contact/chat/chat.page.spec.ts

@@ -0,0 +1,26 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { IonicModule } from '@ionic/angular';
+
+import { ExploreContainerComponentModule } from '../explore-container/explore-container.module';
+
+import { ChatPage } from './chat.page';
+
+describe('ChatPage', () => {
+  let component: ChatPage;
+  let fixture: ComponentFixture<ChatPage>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ChatPage],
+      imports: [IonicModule.forRoot(), ExploreContainerComponentModule]
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(ChatPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 121 - 0
src/app/contact/chat/chat.page.ts

@@ -0,0 +1,121 @@
+import { Component, OnInit } from '@angular/core';
+import { NavController } from '@ionic/angular';
+import { Router } from '@angular/router';
+import { ActivatedRoute } from '@angular/router';
+
+import { FormsModule } from '@angular/forms';
+import Parse from "parse";
+
+@Component({
+  selector: 'app-chat',
+  templateUrl: 'chat.page.html',
+  styleUrls: ['chat.page.scss']
+})
+export class ChatPage implements OnInit{
+  userid: string;
+  messageList:any = []
+  messages = [
+    { type: 'received', avatar: '/assets/img/female.png', time: '10:30 AM', text: 'Hi there!' },
+    { type: 'sent', avatar: '/assets/img/female.png', time: '10:31 AM', text: 'Hey! How are you?' },
+    // Add more messages as needed
+  ];
+  
+  newMessage: string = '';
+
+  constructor(private route: ActivatedRoute) {
+    this.userid = this.route.snapshot.paramMap.get('userid') as string;
+    this.targetId = this.userid
+  }
+
+  contact:Parse.Object|undefined
+  user:Parse.User|undefined
+  targetId:string |undefined
+  async ngOnInit() {
+    this.user =  Parse.User.current()
+    let fromId = Parse.User.current()?.id
+    if(this.userid && fromId){
+      let query = new Parse.Query("Contact");
+      query.include("to")
+      query.equalTo("from",fromId);
+      query.equalTo("to",this.userid);
+      this.contact = await query.first();
+    }
+    this.loadHistory();
+    return
+  }
+  async sendMessage() {
+    if (this.newMessage.trim() === '') {
+      return;
+    }
+    let sendUser = Parse.User?.current()?.toPointer()
+    let receiveUser = {__type:"Pointer",className:"_User",objectId:this.userid}
+    let Message = Parse.Object.extend("Message");
+    let message = new Message();
+    message.set("sendUser",sendUser)
+    message.set("receiveUser",receiveUser)
+    message.set("contentJson",{
+      role:"user",
+      content: this.newMessage
+    })
+    message = await message.save()
+    if(message?.id){
+      this.messageList.push(message)
+      this.syncSession(message,sendUser,receiveUser);
+    }
+    this.newMessage = '';
+
+    this.loadHistory();
+  }
+  async syncSession(message:any,sendUser:any,receiveUser:any){
+    // 根据当前聊天,更新最新的会话状态表 MessageSession 记录最新的会话
+    let session = await this.checkSessionExists();
+    if(!session?.id){
+      let MessageSession = Parse.Object.extend("MessageSession")
+      session = new MessageSession()
+    }
+    session?.set("message",message?.toPointer())
+    session?.set("sendUser",sendUser)
+    session?.set("receiveUser",receiveUser)
+    session?.save();
+  }
+  async checkSessionExists(){
+    let query = Parse.Query.fromJSON('MessageSession',{where: {
+      $or: [
+        {
+          sendUser: this.user?.id,
+          receiveUser: this.targetId
+        },
+        {
+          sendUser: this.targetId,
+          receiveUser: this.user?.id
+        }
+      ]
+    }})
+    return await query.first()
+  }
+
+  getCurrentTime() {
+    const now = new Date();
+    return now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
+  }
+
+  async loadHistory(){
+    let query = Parse.Query.fromJSON('Message',{where: {
+      $or: [
+        {
+          sendUser: this.user?.id,
+          receiveUser: this.targetId
+        },
+        {
+          sendUser: this.targetId,
+          receiveUser: this.user?.id
+        }
+      ]
+    }})
+
+    let list = await query.find();
+    if(list?.length){
+      this.messageList = list
+    }
+  }
+}

+ 17 - 0
src/app/contact/contact-detail/contact-detail-routing.module.ts

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

+ 20 - 0
src/app/contact/contact-detail/contact-detail.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 { ContactDetailPageRoutingModule } from './contact-detail-routing.module';
+
+import { ContactDetailPage } from './contact-detail.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    ContactDetailPageRoutingModule
+  ],
+  declarations: [ContactDetailPage]
+})
+export class ContactDetailPageModule {}

+ 13 - 0
src/app/contact/contact-detail/contact-detail.page.html

@@ -0,0 +1,13 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title>contact-detail</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <ion-header collapse="condense">
+    <ion-toolbar>
+      <ion-title size="large">contact-detail</ion-title>
+    </ion-toolbar>
+  </ion-header>
+</ion-content>

+ 0 - 0
src/app/contact/contact-detail/contact-detail.page.scss


+ 17 - 0
src/app/contact/contact-detail/contact-detail.page.spec.ts

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

+ 15 - 0
src/app/contact/contact-detail/contact-detail.page.ts

@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector: 'app-contact-detail',
+  templateUrl: './contact-detail.page.html',
+  styleUrls: ['./contact-detail.page.scss'],
+})
+export class ContactDetailPage implements OnInit {
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}

+ 17 - 0
src/app/contact/contact-list/contact-list-routing.module.ts

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

+ 20 - 0
src/app/contact/contact-list/contact-list.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 { ContactListPageRoutingModule } from './contact-list-routing.module';
+
+import { ContactListPage } from './contact-list.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    ContactListPageRoutingModule
+  ],
+  declarations: [ContactListPage]
+})
+export class ContactListPageModule {}

+ 68 - 0
src/app/contact/contact-list/contact-list.page.html

@@ -0,0 +1,68 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title>通讯录</ion-title>
+    <ion-buttons slot="end">
+      <ion-button id="add-contact">
+        添加
+      </ion-button>
+    </ion-buttons>
+  </ion-toolbar>
+</ion-header>
+
+<ion-modal trigger="add-contact" #modal>
+  <ng-template>
+    <ion-header>
+      <ion-toolbar>
+        <ion-buttons slot="start">
+          <ion-button>取消</ion-button>
+        </ion-buttons>
+        <ion-title>添加联系人</ion-title>
+        <ion-buttons slot="end">
+          <ion-button (click)="addContact()" [strong]="true">确认</ion-button>
+        </ion-buttons>
+      </ion-toolbar>
+    </ion-header>
+    <ion-content class="ion-padding">
+      <ion-item>
+        <ion-input  label="联系人" placeholder="输入用户名" [(ngModel)]="usernameInput" type="text"></ion-input>
+      </ion-item>
+    </ion-content>
+   
+  </ng-template>
+</ion-modal>
+
+<ion-content [fullscreen]="true">
+  
+  
+  <ion-searchbar [(ngModel)]="searchName" (ionInput)="search()"></ion-searchbar>
+
+  
+  <ion-segment [(ngModel)]="segment" (click)="loadContact()">
+    <ion-segment-button value="me">
+      我的
+    </ion-segment-button>
+    <ion-segment-button value="all">
+      全部
+    </ion-segment-button>
+  </ion-segment>
+
+  <ion-list>
+    <ng-container *ngFor="let contact of contactList;let index = index;">
+      <!-- 分组:根据下标首个出现的元素显示分组分隔符 -->
+      <ion-item-divider *ngIf="charGroupIndex[contact.get('firstChar')] == index">
+        <ion-label>{{contact.get('firstChar')}}</ion-label>
+      </ion-item-divider>
+      <ion-item lines="none">
+        <ion-avatar slot="start">
+          <img [src]="contact.get('avatarUrl') || 'https://ionicframework.com/docs/img/demos/avatar.svg'">
+        </ion-avatar>
+        <ion-label>
+          <h2>{{ contact.get('name') || contact?.get('to')?.get("nickname") ||contact?.get('to')?.get('username') }}</h2>
+          <p>性别: {{ contact.get('gender') || contact?.get('to')?.get("gender") }}</p>
+          <p>手机: {{ contact.get('mobile') || contact?.get('to')?.get("mobile")}}</p>
+        </ion-label>
+        <ion-button slot="end" routerLink="/contact/chat/{{contact?.get('to')?.id}}">聊天</ion-button>
+      </ion-item>
+    </ng-container>
+  </ion-list>
+</ion-content>

+ 0 - 0
src/app/contact/contact-list/contact-list.page.scss


+ 17 - 0
src/app/contact/contact-list/contact-list.page.spec.ts

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

+ 73 - 0
src/app/contact/contact-list/contact-list.page.ts

@@ -0,0 +1,73 @@
+import { Component, OnInit } from '@angular/core';
+import * as Parse from "parse";
+@Component({
+  selector: 'app-contact-list',
+  templateUrl: './contact-list.page.html',
+  styleUrls: ['./contact-list.page.scss'],
+})
+export class ContactListPage implements OnInit {
+  searchName: string = '';
+  contactList: Array<Parse.Object> = [];
+  segment:string = "me";
+  ngOnInit() {
+    this.loadContact();
+  }
+
+  usernameInput:string = ""
+  async checkIfExists(toUser:Parse.Object){
+    let query = new Parse.Query("Contact");
+    query.equalTo("from",Parse.User.current()?.toPointer())
+    query.equalTo("to",toUser.toPointer())
+    return await query.first();
+  }
+  async addContact(){
+    if(this.usernameInput){
+      let query = new Parse.Query("_User");
+      query.equalTo("username",this.usernameInput);
+      let user = await query.first()
+      if(user?.id){
+        let exists = await this.checkIfExists(user)
+        if(exists?.id) return
+        let Contact = Parse.Object.extend("Contact");
+        let contact = new Contact()
+        contact.set("from",Parse.User.current()?.toPointer())
+        contact.set("to",user.toPointer())
+        await contact.save();
+        this.loadContact();
+      }else{
+        // 提示找不到用户
+      }
+    }
+  }
+  charGroupIndex:any = {}
+  loadContact() {
+    const Contact = Parse.Object.extend('Contact');
+    const query = new Parse.Query(Contact);
+    query.include("to")
+    query.ascending('firstChar');
+    if(this.segment=="me"){
+      if(!Parse.User.current()?.id) return
+      query.equalTo("from",Parse.User.current()?.id)
+    }
+
+    if (this.searchName) {
+      query.contains('name', this.searchName);
+    }
+
+    query.find().then((results) => {
+      this.contactList = results;
+      this.contactList.forEach((contact,index)=>{
+        if(this.charGroupIndex[contact.get("firstChar")] == undefined){
+          this.charGroupIndex[contact.get("firstChar")] = index
+        }
+      })
+    }, (error) => {
+      console.error('Error while fetching contacts', error);
+    });
+  }
+
+  search() {
+    this.loadContact();
+  }
+
+}

+ 12 - 0
src/app/contact/contact-routing.module.ts

@@ -0,0 +1,12 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+const routes: Routes = [
+  {path: 'list', loadChildren: () => import('./contact-list/contact-list.module').then(mod => mod.ContactListPageModule)},
+  {path: 'detail/:id', loadChildren: () => import('./contact-detail/contact-detail.module').then(mod => mod.ContactDetailPageModule)},
+];
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class ContactRoutingModule { }

+ 14 - 0
src/app/contact/contact.module.ts

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

+ 14 - 0
src/app/contact/session/session-routing.module.ts

@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { SessionPage } from './session.page';
+// import { Session1Page } from './session copy/session1.page';
+const routes: Routes = [
+  { path: '', component: SessionPage },
+  // { path: 'chat/:username', component: Session1Page },
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class SessionPageRoutingModule {}

+ 20 - 0
src/app/contact/session/session.module.ts

@@ -0,0 +1,20 @@
+import { IonicModule } from '@ionic/angular';
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { SessionPage } from './session.page';
+
+import { SessionPageRoutingModule } from './session-routing.module';
+import { UserNamePipe } from '../user-name.pipe';
+
+@NgModule({
+  imports: [
+    IonicModule,
+    CommonModule,
+    FormsModule,
+    SessionPageRoutingModule,
+    UserNamePipe,
+  ],
+  declarations: [SessionPage]
+})
+export class SessionPageModule {}

+ 37 - 0
src/app/contact/session/session.page.html

@@ -0,0 +1,37 @@
+<ion-header>
+  <ion-toolbar>
+    <ion-title style="text-align: center;">联系人</ion-title>
+  </ion-toolbar>
+  <ion-toolbar>
+    <ion-searchbar class="custom-searchbar" placeholder="搜索联系人"></ion-searchbar>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content>
+  <ion-list lines="full">
+    <ion-item *ngFor="let session of sessionList" (click)="goSession(session)">
+      <ion-avatar slot="start">
+        <img [src]="'/assets/img/clock.png'">
+      </ion-avatar>
+      <ion-label>
+        <h2>{{targetUser(session) | userName}}</h2>
+        <p>{{session?.get('message')?.get("contentJson")?.content}}</p>
+        <p>{{session?.createdAt | date:"MM-dd HH:mm"}}</p>
+      </ion-label>
+    </ion-item>
+  </ion-list>
+
+
+  <ion-list lines="full" *ngIf="!sessionList?.length">
+    <ion-item *ngFor="let item of skeletonList">
+      <ion-avatar slot="start">
+        <img [src]="item.avatar">
+      </ion-avatar>
+      <ion-label>
+        <h2>{{ item.username }}</h2>
+        <p>{{ item.lastMessageTime }}</p>
+        <p>{{ item.lastMessage }}</p>
+      </ion-label>
+    </ion-item>
+  </ion-list>
+</ion-content>

+ 69 - 0
src/app/contact/session/session.page.scss

@@ -0,0 +1,69 @@
+
+.custom-searchbar {
+  --background: #f2f2f2; /* 设置搜索栏的背景色为灰色 */
+  --border-width: 1px; /* 设置边框宽度 */
+  --border-color: #ccc; /* 设置边框颜色 */
+  --border-radius: 10px; /* 设置圆角半径 */
+  --box-shadow: none; /* 去掉阴影 */
+}
+
+.custom-searchbar .searchbar-input {
+  font-size: 14px; /* 设置字体大小 */
+}
+
+
+
+ion-avatar {
+  width: 56px;
+  height: 56px;
+}
+
+ion-item {
+  --ion-item-background: transparent;
+  --ion-item-text-transform: none;
+}
+
+ion-label h2 {
+  font-size: 18px;
+  font-weight: bold;
+}
+
+ion-label p {
+  font-size: 14px;
+  color: #888;
+}
+.header-md{
+  box-shadow: none;
+  border-bottom: 1px rgba(0,0,0,0.2) solid;
+}
+.custom-searchbar {
+  --border-radius: 20px; /* 设置圆角大小 */
+  --height: 10px; /* 设置搜索框高度 */
+  // --box-shadow: none; /* 去掉阴影 */
+  // border:#f0f0f0 8px solid;
+
+  /* 可选:调整输入框内部的样式 */
+  .searchbar-input {
+    border-radius: var(--border-radius);
+    height: var(--height);
+    box-shadow: var(--box-shadow);
+  }
+
+  /* 可选:调整输入框外部的样式 */
+  .searchbar-input-container {
+    border-radius: var(--border-radius);
+    height: var(--height);
+    box-shadow: var(--box-shadow);
+  }
+}
+ion-content {
+  background-color: #f0f0f0; /* 设置整体背景色为白灰色 */
+}
+
+/* 如果想突出显示聊天框,可以为ion-item添加自定义样式 */
+ion-item.chat-item {
+  background-color: #ffffff; /* 设置聊天框的背景色为白色 */
+  border-radius: 10px; /* 可选:设置聊天框的圆角 */
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 可选:添加轻微阴影增强立体感 */
+}
+// .toolbar-con

+ 24 - 0
src/app/contact/session/session.page.spec.ts

@@ -0,0 +1,24 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { IonicModule } from '@ionic/angular';
+
+import { SessionPage } from './session.page';
+
+describe('SessionPage', () => {
+  let component: SessionPage;
+  let fixture: ComponentFixture<SessionPage>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [SessionPage],
+      imports: [IonicModule.forRoot()]
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(SessionPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 69 - 0
src/app/contact/session/session.page.ts

@@ -0,0 +1,69 @@
+import { Component } from '@angular/core';
+import { NavController } from '@ionic/angular';
+import { Router } from '@angular/router';
+import Parse from "parse";
+@Component({
+  selector: 'app-session',
+  templateUrl: 'session.page.html',
+  styleUrls: ['session.page.scss']
+})
+export class SessionPage {
+  skeletonList: { username: string, avatar: string, lastMessageTime: string, lastMessage: string }[] = [
+    {
+      username: 'Alice',
+      avatar: '/assets/img/female.png',
+      lastMessageTime: '10:30 AM',
+      lastMessage: 'Hey, how are you?',
+    },
+    {
+      username: 'Bob',
+      avatar: '/assets/img/male.png',
+      lastMessageTime: 'Yesterday',
+      lastMessage: 'Let\'s meet tomorrow.',
+    },
+  ];
+
+  constructor(private navCtrl: NavController) {
+    this.loadSessions()
+  }
+
+  targetUser(session:Parse.Object){
+    let user = Parse.User.current();
+    let target = null
+    if(session?.get("sendUser")?.id==user?.id){
+      target = session?.get("receiveUser")
+    }
+    if(session?.get("receiveUser")?.id==user?.id){
+      target = session?.get("sendUser")
+    }
+    return target
+  }
+  goSession(session:Parse.Object){
+    let user = Parse.User.current();
+    if(session?.get("sendUser")?.id==user?.id){
+      this.navCtrl.navigateForward(`/contact/chat/${session?.get("receiveUser")?.id}`);
+    }
+    if(session?.get("receiveUser")?.id==user?.id){
+      this.navCtrl.navigateForward(`/contact/chat/${session?.get("sendUser")?.id}`);
+    }
+  }
+  sessionList:Parse.Object[] = []
+  async loadSessions(){
+    console.log("loadSessions")
+    let user = Parse.User.current();
+    let query = Parse.Query.fromJSON('MessageSession',{where: {
+      $or: [
+        {
+          sendUser: user?.id,
+        },
+        {
+          receiveUser: user?.id
+        }
+      ]
+    }})
+    query.include("sendUser","receiveUser");
+    this.sessionList = await query.find();
+  }
+
+
+}

+ 8 - 0
src/app/contact/user-name.pipe.spec.ts

@@ -0,0 +1,8 @@
+import { UserNamePipe } from './user-name.pipe';
+
+describe('UserNamePipe', () => {
+  it('create an instance', () => {
+    const pipe = new UserNamePipe();
+    expect(pipe).toBeTruthy();
+  });
+});

+ 19 - 0
src/app/contact/user-name.pipe.ts

@@ -0,0 +1,19 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+  name: 'userName',
+  pure:true,
+  standalone: true
+})
+export class UserNamePipe implements PipeTransform {
+
+  transform(user: Parse.Object, ...args: unknown[]): unknown {
+    let nickname = user?.get("nickname");
+    let username = user?.get("username");
+    let name = user?.get("name");
+    let mobile = user?.get("mobile");
+    console.log(user?.toJSON())
+    return nickname || name || mobile  || username;
+  }
+
+}

+ 17 - 0
src/app/lostfound/lostfound-routing.module.ts

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

+ 20 - 0
src/app/lostfound/lostfound.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 { LostfoundPageRoutingModule } from './lostfound-routing.module';
+
+import { LostfoundPage } from './lostfound.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    LostfoundPageRoutingModule
+  ],
+  declarations: [LostfoundPage]
+})
+export class LostfoundPageModule {}

+ 27 - 0
src/app/lostfound/lostfound.page.html

@@ -0,0 +1,27 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <div class="item-container">
+      <ion-avatar class="avatar-container">
+        <ion-img [src]="items?.get('headImage')" alt="头像"></ion-img>
+      </ion-avatar>
+      <div class="info-container">
+        <p class="publisher">{{items?.get("publisher")}}</p>
+        <h2 class="title">{{items?.get("title")}}</h2>
+      </div>
+    </div>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <ion-card class="content">
+    <p>物品:{{items?.get("name")}}</p>
+    <p>状态:{{items?.get("status")}}</p>
+    <p>时间:{{items?.get("time")}}</p>
+    <P>地点:{{items?.get("location")}}</P>
+    <p>内容:{{items?.get("content")}}</p>
+    <p>图片:</p>
+    <div class="image-container">
+      <ion-img [src]="items?.get('image')"></ion-img>
+    </div>
+  </ion-card>
+</ion-content>

+ 36 - 0
src/app/lostfound/lostfound.page.scss

@@ -0,0 +1,36 @@
+.item-container {
+    display: flex;
+    align-items: center;
+}
+  
+.avatar-container {
+    margin-left: 10px;
+}
+  
+.info-container {
+    margin-left: 10px;
+}
+
+.title {
+    color: #000000; /* 设置字体颜色为黑色 */
+    font-size: 1.2em; /* 设置字体大小为1.2em */
+}
+
+.content {
+    color: #000000; 
+    font-size: 1.0em; 
+}
+
+.image-container {
+    width: 200px; /* 设置图片容器的宽度 */
+    height: 200px; /* 设置图片容器的高度 */
+    display: flex;
+}
+
+.image-container ion-img {
+    margin-left: 30px;
+    // margin-bottom: 10px;
+    margin-top:-10px;
+    width: 100%;
+    height: 100%;
+}

+ 17 - 0
src/app/lostfound/lostfound.page.spec.ts

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

+ 28 - 0
src/app/lostfound/lostfound.page.ts

@@ -0,0 +1,28 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import Parse from "parse";
+
+@Component({
+  selector: 'app-lostfound',
+  templateUrl: './lostfound.page.html',
+  styleUrls: ['./lostfound.page.scss'],
+})
+export class LostfoundPage implements OnInit {
+
+  constructor(private route:ActivatedRoute) { }
+
+  ngOnInit() {
+    this.loadItemsById()
+  }
+
+  items:Parse.Object|undefined
+  async loadItemsById() {
+    let id = this.route.snapshot.params["id"]
+
+    if(id){
+      let query = new Parse.Query("Items");
+      this.items = await query.get(id);
+    }
+  }
+
+}

+ 17 - 0
src/app/resort/resort-routing.module.ts

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

+ 5 - 5
src/app/tab4/tab11/tab11.module.ts → src/app/resort/resort.module.ts

@@ -4,17 +4,17 @@ import { FormsModule } from '@angular/forms';
 
 import { IonicModule } from '@ionic/angular';
 
-import { Tab11PageRoutingModule } from './tab11-routing.module';
+import { ResortPageRoutingModule } from './resort-routing.module';
 
-import { Tab11Page } from './tab11.page';
+import { ResortPage } from './resort.page';
 
 @NgModule({
   imports: [
     CommonModule,
     FormsModule,
     IonicModule,
-    Tab11PageRoutingModule,
+    ResortPageRoutingModule
   ],
-  declarations: [Tab11Page]
+  declarations: [ResortPage]
 })
-export class Tab11PageModule {}
+export class ResortPageModule {}

+ 25 - 0
src/app/resort/resort.page.html

@@ -0,0 +1,25 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <div class="item-container">
+      <ion-avatar class="avatar-container">
+        <ion-img [src]="items?.get('headImage')" alt="头像"></ion-img>
+      </ion-avatar>
+      <div class="info-container">
+        <p class="publisher">{{items?.get("publisher")}}</p>
+        <h2 class="title">{{items?.get("title")}}</h2>
+      </div>
+    </div>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <ion-card class="content">
+    <!-- <p>产品:{{items?.get("name")}}</p>
+    <P>地点:{{items?.get("location")}}</P> -->
+    <p>内容:{{items?.get("content")}}</p>
+    <!-- <p>图片:</p>
+    <div class="image-container">
+      <ion-img [src]="items?.get('image')"></ion-img>
+    </div> -->
+  </ion-card>
+</ion-content>

+ 36 - 0
src/app/resort/resort.page.scss

@@ -0,0 +1,36 @@
+.item-container {
+    display: flex;
+    align-items: center;
+}
+  
+.avatar-container {
+    margin-left: 10px;
+}
+  
+.info-container {
+    margin-left: 10px;
+}
+
+.title {
+    color: #000000; /* 设置字体颜色为黑色 */
+    font-size: 1.2em; /* 设置字体大小为1.2em */
+}
+
+.content {
+    color: #000000; 
+    font-size: 1.0em; 
+}
+
+.image-container {
+    width: 200px; /* 设置图片容器的宽度 */
+    height: 200px; /* 设置图片容器的高度 */
+    display: flex;
+}
+
+.image-container ion-img {
+    margin-left: 30px;
+    // margin-bottom: 10px;
+    margin-top:-10px;
+    width: 100%;
+    height: 100%;
+}

+ 5 - 5
src/app/tab4/tab14/tab14.page.spec.ts → src/app/resort/resort.page.spec.ts

@@ -1,12 +1,12 @@
 import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { Tab14Page } from './tab14.page';
+import { ResortPage } from './resort.page';
 
-describe('Tab14Page', () => {
-  let component: Tab14Page;
-  let fixture: ComponentFixture<Tab14Page>;
+describe('ResortPage', () => {
+  let component: ResortPage;
+  let fixture: ComponentFixture<ResortPage>;
 
   beforeEach(() => {
-    fixture = TestBed.createComponent(Tab14Page);
+    fixture = TestBed.createComponent(ResortPage);
     component = fixture.componentInstance;
     fixture.detectChanges();
   });

+ 28 - 0
src/app/resort/resort.page.ts

@@ -0,0 +1,28 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import Parse from "parse";
+
+@Component({
+  selector: 'app-resort',
+  templateUrl: './resort.page.html',
+  styleUrls: ['./resort.page.scss'],
+})
+export class ResortPage implements OnInit {
+
+  constructor(private route:ActivatedRoute) { }
+
+  ngOnInit() {
+    this.loadItemsById()
+  }
+
+  items:Parse.Object|undefined
+  async loadItemsById() {
+    let id = this.route.snapshot.params["id"]
+
+    if(id){
+      let query = new Parse.Query("Items");
+      this.items = await query.get(id);
+    }
+  }
+
+}

+ 17 - 0
src/app/tab3/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/app/tab3/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 {}

+ 44 - 0
src/app/tab3/edit-info/edit-info.page.html

@@ -0,0 +1,44 @@
+<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">edit-info</ion-title>
+    </ion-toolbar>
+  </ion-header>
+  <div >
+    <ion-item>
+      <ion-label position="stacked">用户名</ion-label>
+      <ion-input [(ngModel)]="editedUser.name" type="text"></ion-input>
+    </ion-item>
+    <ion-item (click)="fileInput.click()">
+      <ion-label position="stacked">上传新头像</ion-label>
+      <input type="file" #fileInput style="display: none;" (change)="onFileSelected($event)">
+      <ion-avatar class="avatar-right" style="cursor: pointer;">
+        <!-- <img [src]="editedUser.avatarUrl || user.avatarUrl" alt="Avatar"> -->
+      </ion-avatar>
+    </ion-item>
+    <ion-item>
+      <ion-label position="stacked">楼栋号</ion-label>
+      <ion-input [(ngModel)]="editedUser.building" type="text"></ion-input>
+    </ion-item>
+    <ion-item>
+      <ion-label position="stacked">校区名称</ion-label>
+      <ion-input [(ngModel)]="editedUser.campus" type="text"></ion-input>
+    </ion-item>
+    <ion-item>
+      <ion-label>性别</ion-label>
+      <ion-select [(ngModel)]="editedUser.gender">
+        <ion-select-option value="male">男</ion-select-option>
+        <ion-select-option value="female">女</ion-select-option>
+        <ion-select-option value="other">其他</ion-select-option>
+      </ion-select>
+    </ion-item>
+    <ion-button expand="block" color="primary" (click)="save()" >保存</ion-button>
+    <ion-button expand="block" color="danger" (click)="cancelEditing()">取消</ion-button>
+  </div>
+</ion-content>

+ 0 - 0
src/app/tab3/edit-info/edit-info.page.scss


+ 17 - 0
src/app/tab3/edit-info/edit-info.page.spec.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();
+  });
+});

+ 72 - 0
src/app/tab3/edit-info/edit-info.page.ts

@@ -0,0 +1,72 @@
+import { Component, OnInit } from '@angular/core';
+import { NavController } from '@ionic/angular';
+import 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: '',
+    avatarUrl: 'assets/美津未.jpg',
+    campus: '',
+    building: '',
+    gender: ''
+  };
+  editedUser = { ...this.userInfo }; // Clone of user for editing
+  editing = false;
+  currentUser:Parse.User|undefined
+  // navController: any;
+  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)
+  }
+  onFileSelected(event: any) {
+    const file: File = event.target.files[0];
+    const reader = new FileReader();
+    reader.onload = () => {
+      this.editedUser.avatarUrl = reader.result as string;
+    };
+    reader.readAsDataURL(file);
+  }
+
+  startEditing() {
+    this.editing = true;
+  }
+  save() {
+    this.currentUser = Parse.User.current();
+    if (this.currentUser) {
+      console.log(this.editedUser);
+      this.editedUser.birthday = this.editedUser.birthday || new Date();
+      this.editedUser.birthday = new Date(this.editedUser.birthday);
+      for (const key in this.editedUser) {
+        if (this.editedUser.hasOwnProperty(key)) {
+          this.currentUser.set(key, this.editedUser[key]);
+        }
+      }
+      this.currentUser.save().then(() => {
+        this.navController.back();
+      }).catch((error) => {
+        console.error('Error saving user data: ', error);
+      });
+    }
+    console.log(this.editedUser);
+}
+
+  cancelEditing() {
+    this.navController.back();
+  }
+
+}

+ 26 - 14
src/app/tab3/login/login.page.html

@@ -1,21 +1,33 @@
-<ion-header>
+<ion-header [translucent]="true">
   <ion-toolbar>
-    <ion-title>
-      Login
-    </ion-title>
+    <ion-title>登录/注册</ion-title>
   </ion-toolbar>
 </ion-header>
 
-<ion-content class="ion-padding">
-  <ion-item>
-    <ion-label position="floating">Username</ion-label>
-    <ion-input type="text" [(ngModel)]="username"></ion-input>
-  </ion-item>
+<ion-content [fullscreen]="true">
 
-  <ion-item>
-    <ion-label position="floating">Password</ion-label>
-    <ion-input type="password" [(ngModel)]="password"></ion-input>
-  </ion-item>
+  <ion-card>
+    <ion-card-header>
+      <ion-card-title>登录/注册</ion-card-title>
+    </ion-card-header>
+  
+    <ion-card-content>
 
-  <ion-button expand="block" (click)="login()">Login</ion-button>
+      <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>

+ 92 - 14
src/app/tab3/login/login.page.ts

@@ -1,5 +1,8 @@
 import { Component, OnInit } from '@angular/core';
-import { Router, RouterModule, Routes } from '@angular/router';
+import { NavController, AlertController } from '@ionic/angular';
+import { ChangeDetectorRef } from '@angular/core';
+
+import * as Parse from "parse";
 
 @Component({
   selector: 'app-login',
@@ -8,22 +11,97 @@ import { Router, RouterModule, Routes } from '@angular/router';
 })
 export class LoginPage implements OnInit {
 
-  username: string|undefined;
-  password: string|undefined;
-
-  constructor(private router: Router) {}
+  username:string = ""
+  password:string = ""
+  isLoggedin: boolean=false;
+  // isLoggedin: boolean;
+  // isLoggedIn: boolean = false; 
+  constructor(
+    // 新增:Router服务,用于路由跳转
+    private navCtrl:NavController,
+    private alertController:AlertController
+  ) { }
 
-  ngOnInit() {}
+  ngOnInit() {
+  }
 
-  login() {
-    // 实现登录逻辑,这里简单演示,你可以根据需要连接实际的身份验证服务
-    if (this.username === 'admin' && this.password === 'password') {
-      // 登录成功,跳转到另一个页面(例如主页)
-      this.router.navigate(['/home']);
-    } else {
-      // 处理登录失败的情况,例如显示错误消息
-      console.log('Login failed');
+  async login(){
+    let user
+    try {
+      user = await Parse.User.logIn(this.username,this.password)
+      // this.isLoggedin = true;
+      if (user?.id) {
+        this.navCtrl.navigateForward(['/tabs/tab3', { isLoggedIn: true }]);
+      }
+    } 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 (user?.id) {
+          this.navCtrl.navigateForward(['/tabs/tab3', { isLoggedIn: true }]);
+        }
+        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/app/tab3/nologin/nologin-routing.module.ts

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

+ 5 - 5
src/app/tab4/tab14/tab14.module.ts → src/app/tab3/nologin/nologin.module.ts

@@ -4,17 +4,17 @@ import { FormsModule } from '@angular/forms';
 
 import { IonicModule } from '@ionic/angular';
 
-import { Tab14PageRoutingModule } from './tab14-routing.module';
+import { NologinPageRoutingModule } from './nologin-routing.module';
 
-import { Tab14Page } from './tab14.page';
+import { NologinPage } from './nologin.page';
 
 @NgModule({
   imports: [
     CommonModule,
     FormsModule,
     IonicModule,
-    Tab14PageRoutingModule
+    NologinPageRoutingModule
   ],
-  declarations: [Tab14Page]
+  declarations: [NologinPage]
 })
-export class Tab14PageModule {}
+export class NologinPageModule {}

+ 13 - 0
src/app/tab3/nologin/nologin.page.html

@@ -0,0 +1,13 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title>nologin</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <ion-header collapse="condense">
+    <ion-toolbar>
+      <ion-title size="large">nologin</ion-title>
+    </ion-toolbar>
+  </ion-header>
+</ion-content>

+ 0 - 0
src/app/tab3/nologin/nologin.page.scss


+ 5 - 5
src/app/tab4/tab11/tab11.page.spec.ts → src/app/tab3/nologin/nologin.page.spec.ts

@@ -1,12 +1,12 @@
 import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { Tab11Page } from './tab11.page';
+import { NologinPage } from './nologin.page';
 
-describe('Tab11Page', () => {
-  let component: Tab11Page;
-  let fixture: ComponentFixture<Tab11Page>;
+describe('NologinPage', () => {
+  let component: NologinPage;
+  let fixture: ComponentFixture<NologinPage>;
 
   beforeEach(() => {
-    fixture = TestBed.createComponent(Tab11Page);
+    fixture = TestBed.createComponent(NologinPage);
     component = fixture.componentInstance;
     fixture.detectChanges();
   });

+ 15 - 0
src/app/tab3/nologin/nologin.page.ts

@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector: 'app-nologin',
+  templateUrl: './nologin.page.html',
+  styleUrls: ['./nologin.page.scss'],
+})
+export class NologinPage implements OnInit {
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}

+ 5 - 0
src/app/tab3/tab3-routing.module.ts

@@ -1,11 +1,16 @@
 import { NgModule } from '@angular/core';
 import { RouterModule, Routes } from '@angular/router';
 import { Tab3Page } from './tab3.page';
+import { EditInfoPage } from './edit-info/edit-info.page';
 
 const routes: Routes = [
   {
     path: '',
     component: Tab3Page,
+  },
+  {
+    path:"edit-info",
+    component:EditInfoPage,
   }
 ];
 

+ 37 - 70
src/app/tab3/tab3.page.html

@@ -1,31 +1,33 @@
 <ion-header [translucent]="true">
-  <!-- <ion-toolbar>
-    <ion-title>个人信息</ion-title>
-  </ion-toolbar> -->
 </ion-header>
-
 <ion-content>
-  <div *ngIf="!editing">
-    <!-- 显示个人信息界面 -->
+
+  <!-- 根据登录状态显示不同界面 -->
+  <div *ngIf="!currentUser?.id"> <!-- 如果没有登录显示未登录界面 -->
+    <ion-card>
+      <ion-card-header>
+        <ion-card-title>未登录</ion-card-title>
+        <ion-card-subtitle>请您登录后继续使用</ion-card-subtitle>
+      </ion-card-header>
+      <ion-button fill="clear" routerLink="/login">登录</ion-button>
+    </ion-card>
+  </div>
+
+  <div *ngIf="currentUser?.id"> <!-- 如果登录成功显示个人信息界面 -->
     <div class="profile-header">
       <div class="avatar-section">
         <input type="file" (change)="onFileSelected($event)" accept="image/*" id="fileInput" hidden>
         <label for="fileInput">
           <img [src]="user.avatarUrl" alt="Avatar" class="avatar-img">
-          <ion-icon name="create-outline" class="edit-icon" (click)="startEditing()"></ion-icon>
+          <ion-icon name="create-outline" class="edit-icon" routerLink="/edit-info"></ion-icon>
         </label>
       </div>
       <div class="profile-details">
-        <!-- 用户名 -->
-        <div class="username">{{ user.username }}</div>
-
-        <!-- 校区和楼栋 -->
+        <div class="username">{{user.name}}</div>
         <div class="campus-building">
           <div class="campus">{{ user.campus }}</div>
           <div class="building">{{ user.building }}</div>
         </div>
-
-        <!-- 性别 -->
         <div class="gender">
           <ion-icon [name]="user.gender === 'male' ? 'man' : 'woman'"></ion-icon>
           <div>{{ user.gender === 'male' ? '男' : '女' }}</div>
@@ -34,66 +36,31 @@
     </div>
   </div>
 
-  <!-- 编辑个人信息界面 -->
-<div *ngIf="editing">
-  <ion-item>
-    <ion-label position="stacked">用户名</ion-label>
-    <ion-input [(ngModel)]="editedUser.username" type="text"></ion-input>
+<!-- 其他功能条目 -->
+<ion-list lines="full" class="ion-no-margin" >
+  <ion-item routerLink="/mine/completed">
+    <ion-icon slot="start" name="checkmark-circle"></ion-icon>
+    <ion-label>已完成</ion-label>
+    <ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
   </ion-item>
-  <ion-item (click)="fileInput.click()">
-    <ion-label position="stacked">上传新头像</ion-label>
-    <input type="file" #fileInput style="display: none;" (change)="onFileSelected($event)">
-    <ion-avatar class="avatar-right" style="cursor: pointer;">
-      <img [src]="editedUser.avatarUrl || user.avatarUrl" alt="Avatar">
-    </ion-avatar>
+  <ion-item routerLink="/mine/in-progress">
+    <ion-icon slot="start" name="hourglass"></ion-icon>
+    <ion-label>进行中</ion-label>
+    <ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
   </ion-item>
-  <ion-item>
-    <ion-label position="stacked">楼栋号</ion-label>
-    <ion-input [(ngModel)]="editedUser.building" type="text"></ion-input>
+  <ion-item routerLink="/mine/not-completed">
+    <ion-icon slot="start" name="close-circle"></ion-icon>
+    <ion-label>未完成</ion-label>
+    <ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
   </ion-item>
-  <ion-item>
-    <ion-label position="stacked">校区名称</ion-label>
-    <ion-input [(ngModel)]="editedUser.campus" type="text"></ion-input>
+  <ion-item routerLink="/mine/pending">
+    <ion-icon slot="start" name="alert-circle"></ion-icon>
+    <ion-label>待接单</ion-label>
+    <ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
   </ion-item>
-  <ion-item>
-    <ion-label>性别</ion-label>
-    <ion-select [(ngModel)]="editedUser.gender">
-      <ion-select-option value="male">男</ion-select-option>
-      <ion-select-option value="female">女</ion-select-option>
-      <ion-select-option value="other">其他</ion-select-option>
-    </ion-select>
+  <ion-item (click)="logout()" class="logout-button">
+    <ion-icon slot="start" routerLink="/login" name="log-out"></ion-icon>
+    <ion-label>退出登录</ion-label>
   </ion-item>
-  <ion-button expand="block" color="primary" (click)="saveChanges()">保存</ion-button>
-  <ion-button expand="block" color="danger" (click)="cancelEditing()">取消</ion-button>
-</div>
-
-  
-
-  <!-- 其他功能条目 -->
-  <ion-list lines="full" class="ion-no-margin" *ngIf="!editing">
-    <ion-item routerLink="/mine/completed">
-      <ion-icon slot="start" name="checkmark-circle"></ion-icon>
-      <ion-label>已完成</ion-label>
-      <ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
-    </ion-item>
-    <ion-item routerLink="/mine/in-progress">
-      <ion-icon slot="start" name="hourglass"></ion-icon>
-      <ion-label>进行中</ion-label>
-      <ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
-    </ion-item>
-    <ion-item routerLink="/mine/not-completed">
-      <ion-icon slot="start" name="close-circle"></ion-icon>
-      <ion-label>未完成</ion-label>
-      <ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
-    </ion-item>
-    <ion-item routerLink="/mine/pending">
-      <ion-icon slot="start" name="alert-circle"></ion-icon>
-      <ion-label>待接单</ion-label>
-      <ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
-    </ion-item>
-    <ion-item (click)="logout()" class="logout-button">
-      <ion-icon slot="start" name="log-out"></ion-icon>
-      <ion-label>退出登录</ion-label>
-    </ion-item>
-  </ion-list>
+</ion-list>
 </ion-content>

+ 57 - 16
src/app/tab3/tab3.page.ts

@@ -1,9 +1,8 @@
 import { Component } from '@angular/core';
-// import { Component } from '@angular/core';
 import { Router } from '@angular/router';
-// import { AuthService } from '../../services/auth.service';
-import { NavController } from '@ionic/angular';
 import { AlertController } from '@ionic/angular';
+// import { AuthService } from './AuthService';
+import Parse from "parse"
 
 @Component({
   selector: 'app-tab3',
@@ -11,24 +10,54 @@ import { AlertController } from '@ionic/angular';
   styleUrls: ['tab3.page.scss']
 })
 export class Tab3Page {
-  isLoggedIn: boolean = false; // Assuming initial state is not logged in
-  Editing: boolean = false;
+  isLoggedIn: boolean = false; 
+  // isLoggedIn: boolean = false;
   user = {
-    username: '张三',
+    name: 'John Doe',
     avatarUrl: 'assets/美津未.jpg',
-    campus: '东校区',
-    building: '教学楼A',
-    gender: ''
+    campus: 'Main Campus',
+    building: 'A',
+    gender: 'male'
   };
-
+  // isLoggedin: boolean = false;
   editedUser = { ...this.user }; // Clone of user for editing
-
-  editing = false;
+  //editing = false;
 
   constructor(
     private router: Router,
     private alertController: AlertController
-  ) {}
+  ) {
+    const navigation = this.router.getCurrentNavigation();
+    const state = navigation?.extras.state;
+    if (state && state['isLoggedIn']) {
+      this.isLoggedIn = true;
+    }
+  }
+  
+
+  currentUser:Parse.User|undefined
+
+  async ngOnInit() {
+    this.currentUser = await Parse.User.current();
+    if (this.currentUser) {
+      // 如果有当前用户信息,则更新user对象
+      this.user.name = this.currentUser.get("name");
+      this.user.gender=this.currentUser.get("gender");
+      // 根据你的数据库字段来更新其他用户信息
+      // this.user.avatarUrl = this.currentUser.get("avatarUrl");
+      this.user.campus = this.currentUser.get("campus");
+      this.user.building = this.currentUser.get("building");
+      // this.user.gender = this.username.get("gender");
+      console.log(this.user.name);
+      //detectChanges();
+    }
+  }
+  // async ngOnInit() {
+  //   this.username = await Parse.User.current()
+  //   setInterval(async ()=>{
+  //   this.username = await Parse.User.current()
+  // },1000)
+
 
   onFileSelected(event: any) {
     const file: File = event.target.files[0];
@@ -40,23 +69,35 @@ export class Tab3Page {
   }
 
   startEditing() {
-    this.editing = true;
+    //this.editing = true;
   }
 
   saveChanges() {
     // Save changes to user object and exit editing mode
     this.user = { ...this.editedUser };
-    this.editing = false;
+    //this.editing = false;
     // Optionally, you can add logic here to save changes to backend
   }
 
   cancelEditing() {
     // Exit editing mode without saving changes
     this.editedUser = { ...this.user }; // Reset editedUser to original user
-    this.editing = false;
+    //this.editing = false;
   }
 
+  // login() {
+  //   // Handle login logic here, for example navigate to login page
+  //   this.router.navigate(['/user/login']);
+  // }
+
   logout() {
     // Handle logout logic here
+     Parse.User.logOut();
+     console.log("退出登入!");
+     
   }
 }
+
+function detectChanges() {
+  throw new Error('Function not implemented.');
+}

+ 0 - 16
src/app/tab4/data.service .spec.ts

@@ -1,16 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-
-import { DataService } from './data.service';
-
-describe('DataService', () => {
-  let service: DataService;
-
-  beforeEach(() => {
-    TestBed.configureTestingModule({});
-    service = TestBed.inject(DataService);
-  });
-
-  it('should be created', () => {
-    expect(service).toBeTruthy();
-  });
-});

+ 0 - 121
src/app/tab4/data.service.ts

@@ -1,121 +0,0 @@
-import { Injectable } from '@angular/core';
-
-@Injectable({
-  providedIn: 'root'
-})
-export class DataService {
-
-  publishedItems: any[] = [
-    { id: 1, 
-      category: '求助', 
-      route: '/tab11', 
-      title: '求助!高数', 
-      publisher: '小田' ,
-      headImage:'/assets/images/start.png',
-      timestamp:new Date('2022-05-06T10:30:00'),
-      content: '期末快到了,求大神救救!求重点!',
-      replies: [
-      { floor:1, content: '求考试范围(哭)', sender: '燕燕' },
-      { floor:2, content: '1楼+1', sender: '小万' },
-    ]},
-  ]
-  tradeItems: any[] = [
-    { id: 1, 
-      category: '二手交易', 
-      route: '/tab12', 
-      title: 'ipad9二手出售',
-      publisher: '燕燕' , 
-      headImage:'/assets/images/play.png',
-      timestamp:new Date('2022-03-16'),
-      product: 'ipad9', 
-      amount: '1000', 
-      location: '枫林园宿舍1栋门口',
-      image: '/assets/images/ipad9.jpg',
-      remark: '九成新,心诚者可聊。',
-      replies: [
-      { floor:1, content: '价格还有少吗?', sender: '李小珊' },
-    ]},
-  ];
-  lostFoundItems: any[] = [
-    { id: 1, 
-      category: '失物招领', 
-      route: '/tab13', 
-      title: '李小珊学生证', 
-      publisher: '小万' ,
-      headImage:'/assets/images/rgb.png',
-      timestamp:new Date('2022-08-06T09:31:25'),
-      item: '学生证', 
-      status: '拾取', 
-      location: '枫林园物华楼大厅', 
-      time: '2024-07-01', 
-      image: '/assets/images/学生证.jpg', 
-      remark: '请认识李小珊的同学尽快联系我',
-      replies: []},
-  ];
-  activityItems: any[] = [
-    { id: 1, 
-      category: '活动', 
-      route: '/tab14', 
-      title: '英语六级讲座', 
-      publisher: '耶耶' ,
-      headImage:'/assets/images/black.png',
-      timestamp:new Date('2022-03-01T22:13:21'),
-      name: '英语六级讲座', 
-      target: '所有人', 
-      startTime: '2024-07-10 15:00', 
-      duration: '2 hours', 
-      location: '青云楼报告厅', 
-      remark: '有意愿的同学请准时参加' ,
-      replies: []},
-  ];
-
-  constructor() { }
-
-  //求助
-  addReply(itemId: number, reply: any) {
-    const item = this.publishedItems.find(item => item.id === itemId);
-    if (item) {
-      reply.floor = item.replies.length + 1;
-      item.replies.push(reply);
-    }
-  }
-  getPublishedItems() {
-    return this.publishedItems;
-  }
-
-  //二手交易
-  addReplyToTrade(itemId: number, reply: any) {
-    const item = this.tradeItems.find(item => item.id === itemId);
-    if (item) {
-      reply.floor = item.replies.length + 1;
-      item.replies.push(reply);
-    }
-  }
-  getTradeItems() {
-    return this.tradeItems;
-  }
-
-  //失物招领
-  addReplyToLostFoundItems(itemId: number, reply: any) {
-    const item = this.lostFoundItems.find(item => item.id === itemId);
-    if (item) {
-      reply.floor = item.replies.length + 1;
-      item.replies.push(reply);
-    }
-  }
-  getLostFoundItems() {
-    return this.lostFoundItems;
-  }
-
-  //活动
-  addReplyActivityItems(itemId: number, reply: any) {
-    const item = this.activityItems.find(item => item.id === itemId);
-    if (item) {
-      reply.floor = item.replies.length + 1;
-      item.replies.push(reply);
-    }
-  }
-  getActivityItems() {
-    return this.activityItems;
-  }
-}

+ 76 - 0
src/app/tab4/items.service.ts

@@ -0,0 +1,76 @@
+import { Injectable } from '@angular/core';
+import Parse from 'parse';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class ItemsService {
+  private Items = Parse.Object.extend('Items');
+
+  constructor() {}
+
+  async getItems(queryParams: any): Promise<Parse.Object[]> {
+    const query = new Parse.Query(this.Items);
+    if (queryParams.title) {
+      query.contains('title', queryParams.title);
+    }//标题 string
+    if (queryParams.category) {
+      query.equalTo('category', queryParams.category);
+    }//类型:”求助“”二手交易“”失物招领“”活动“ string
+    if (queryParams.publisher) {
+      query.equalTo('publisher', queryParams.publisher);
+    }//发布人 string
+    if (queryParams.headImage) {
+      query.equalTo('headImage', queryParams.headImage);
+    }//头像 string
+    if (queryParams.image) {
+      query.equalTo('image', queryParams.image);
+    }//图片 string
+    if (queryParams.content) {
+      query.equalTo('content', queryParams.content);
+    }//内容 string
+    if (queryParams.amount) {
+      query.equalTo('amount', queryParams.amount);
+    }//金额 Number
+    if (queryParams.location) {
+      query.equalTo('location', queryParams.location);
+    }//地点 string
+    if (queryParams.duration) {
+      query.equalTo('duration', queryParams.duration);
+    }//持续时长 Number
+    if (queryParams.startDate) {
+      query.equalTo('startDate', queryParams.startDate);
+    }//开始时间 Date
+    if (queryParams.status) {
+      query.equalTo('status', queryParams.status);
+    }//丢失或拾取 string
+    if (queryParams.target) {
+      query.equalTo('target', queryParams.target);
+    }//参与对象 string
+    if (queryParams.time) {
+      query.equalTo('time', queryParams.time);
+    }//时间 Date
+    if (queryParams.name) {
+      query.equalTo('name', queryParams.name);
+    }//名称 string
+    if (queryParams.repliesId) {
+      query.equalTo('repliesId', queryParams.repliesId);
+    }//回复 string
+    // query.contains('title', queryParams.title);
+    // query.equalTo('category', queryParams.category);
+    // query.equalTo('publisher', queryParams.publisher);
+    // query.equalTo('headImage', queryParams.headImage);
+    // query.equalTo('image', queryParams.image);
+    // query.equalTo('content', queryParams.content);
+    // query.equalTo('amount', queryParams.amount);
+    // query.equalTo('location', queryParams.location);
+    // query.equalTo('duration', queryParams.duration);
+    // query.equalTo('startDate', queryParams.startDate);
+    // query.equalTo('status', queryParams.status);
+    // query.equalTo('target', queryParams.target);
+    // query.equalTo('time', queryParams.time);
+    // query.equalTo('name', queryParams.name);
+    // query.equalTo('repliesId', queryParams.repliesId);
+    return await query.find();
+  }
+}

+ 0 - 22
src/app/tab4/tab11/tab11-routing.module.ts

@@ -1,22 +0,0 @@
-import { NgModule } from '@angular/core';
-import { Routes, RouterModule } from '@angular/router';
-
-import { Tab11Page } from './tab11.page';
-
-const newLocal = './tab1/tab11/tab11.module';
-const routes: Routes = [
-  {
-    path: '',
-    component: Tab11Page
-  },
-  {
-    path: 'tab11/:id',
-    loadChildren: () => import(newLocal).then( m => m.Tab11PageModule)
-  },
-];
-
-@NgModule({
-  imports: [RouterModule.forChild(routes)],
-  exports: [RouterModule],
-})
-export class Tab11PageRoutingModule {}

+ 0 - 62
src/app/tab4/tab11/tab11.page.html

@@ -1,62 +0,0 @@
-<ion-header [translucent]="true">
-  <ion-toolbar>
-    <ion-title>
-      <ion-grid>
-        <ion-row>
-          <ion-col size="2">
-            <ion-avatar>
-              <ion-img *ngIf="publishedItems.length > 0" [src]="publishedItems[0].headImage" class="avatar"></ion-img>
-              <ion-icon *ngIf="publishedItems.length === 0" name="person-circle-outline" class="default-avatar"></ion-icon>
-            </ion-avatar>
-          </ion-col>
-          <ion-col size="5">
-            <ion-label>
-              <ion-note>{{publishedItems.length > 0 ? publishedItems[0].publisher : 'tab11'}}</ion-note>
-              <p>发布时间: {{publishedItems.length > 0 ? publishedItems[0].timestamp : 'N/A'}}</p>
-            </ion-label>
-          </ion-col>
-        </ion-row>
-        <ion-row>
-          <ion-col size="12">
-            <h2>{{publishedItems.length > 0 ? publishedItems[0].title : 'tab11'}}</h2>
-          </ion-col>
-        </ion-row>
-      </ion-grid>
-    </ion-title>
-  </ion-toolbar>
-</ion-header>
-
-<ion-content>
-  <ion-list>
-    <!-- 显示信息的部分 -->
-    <ion-item *ngFor="let item of publishedItems">
-      <ion-label>
-        <h2>问题: {{item.content}}</h2>
-      </ion-label>
-    </ion-item>
-  </ion-list>
-  <ion-item *ngFor="let item of publishedItems">
-    <ion-list>
-      <!-- 显示回复内容 -->
-      <p>对话内容:</p>
-      <div *ngIf="item.replies && item.replies.length > 0">
-        <ul>
-          <li *ngFor="let reply of item.replies">
-            <p>{{reply.floor}} 楼 {{reply.sender}}: {{reply.content}}</p>
-          </li>
-        </ul>
-      </div>
-    </ion-list>
-  </ion-item> 
-  <ion-list> 
-    <!-- 回复框 -->
-    <ion-item *ngFor="let item of publishedItems">
-      <ion-item>
-        <ion-textarea placeholder="输入回复内容" [(ngModel)]="replyContent"></ion-textarea>
-      </ion-item>
-      <ion-button (click)="submitReply(item)">提交回复</ion-button>
-    </ion-item>
-  </ion-list>
-  <!-- 返回按钮 -->
-  <!-- <ion-button (click)="goBackToTab1()">返回</ion-button> -->
-</ion-content>

+ 0 - 6
src/app/tab4/tab11/tab11.page.scss

@@ -1,6 +0,0 @@
-ion-button {
-    position: absolute;
-    top: 0;
-    right: 0;
-    margin: 10px;
-}

+ 0 - 34
src/app/tab4/tab11/tab11.page.ts

@@ -1,34 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { DataService } from '../data.service';
-import { Title } from '@angular/platform-browser';
-import { Router } from '@angular/router';
-
-@Component({
-  selector: 'app-tab11',
-  templateUrl: './tab11.page.html',
-  styleUrls: ['./tab11.page.scss'],
-})
-
-export class Tab11Page implements OnInit {
-
-  replyContent: string = '';
-  publishedItems: any[] = [];
-
-  constructor(private dataService: DataService,private titleService: Title,private router: Router) {}
-
-  ngOnInit() {
-    this.publishedItems = this.dataService.getPublishedItems();
-    if (this.publishedItems.length > 0) {
-      this.titleService.setTitle(this.publishedItems[0].title);
-    }
-  }
-
-  submitReply(item: any) {
-    if (this.replyContent.trim() !== '') {
-      const reply = { content: this.replyContent, sender: '小序', floor: 1 }; // 可以根据实际情况设置回复人,初始化楼层号
-      this.dataService.addReply(item.id, reply);
-      this.replyContent = ''; // 清空回复框
-    }
-  }
-
-}

+ 0 - 22
src/app/tab4/tab12/tab12-routing.module.ts

@@ -1,22 +0,0 @@
-import { NgModule } from '@angular/core';
-import { Routes, RouterModule } from '@angular/router';
-
-import { Tab12Page } from './tab12.page';
-
-const newLocal = './tab1/tab12/tab12.module';
-const routes: Routes = [
-  {
-    path: '',
-    component: Tab12Page
-  },
-  {
-    path: 'tab12/:id',
-    loadChildren: () => import(newLocal).then( m => m.Tab12PageModule)
-  },
-];
-
-@NgModule({
-  imports: [RouterModule.forChild(routes)],
-  exports: [RouterModule],
-})
-export class Tab12PageRoutingModule {}

+ 0 - 65
src/app/tab4/tab12/tab12.page.html

@@ -1,65 +0,0 @@
-<ion-header [translucent]="true">
-  <ion-toolbar>
-    <ion-title>
-      <ion-grid>
-        <ion-row>
-          <ion-col size="2">
-            <ion-avatar>
-              <ion-img *ngIf="tradeItems.length > 0" [src]="tradeItems[0].headImage" class="avatar"></ion-img>
-              <ion-icon *ngIf="tradeItems.length === 0" name="person-circle-outline" class="default-avatar"></ion-icon>
-            </ion-avatar>
-          </ion-col>
-          <ion-col size="10">
-            <ion-label>
-              <p>{{tradeItems.length > 0 ? tradeItems[0].publisher : 'tab11'}}</p>
-              <p>发布时间: {{tradeItems.length > 0 ? tradeItems[0].timestamp : 'N/A'}}</p>
-              <ion-label style="display: flex; justify-content: flex-end;">
-                <span style="color:red">¥{{tradeItems.length > 0 ? tradeItems[0].amount : '0'}}</span>
-              </ion-label>
-            </ion-label>
-          </ion-col>
-        </ion-row>
-        <ion-row>
-          <ion-col size="12">
-            <h2>{{tradeItems.length > 0 ? tradeItems[0].title : 'tab12'}}</h2>
-          </ion-col>
-        </ion-row>
-      </ion-grid>
-    </ion-title>
-  </ion-toolbar>
-</ion-header>
-
-<ion-content>
-  <ion-item *ngFor="let item of tradeItems">
-    <ion-list>
-      <p>产品: {{item.product}}</p>
-      <p>金额: {{item.amount}}</p>
-      <p>地点: {{item.location}}</p>
-      <p>图片:</p>
-         <p><img *ngIf="item.image" [src]="item.image" style="max-width: 100px;"></p>
-      <p>备注: {{item.remark}}</p>
-    </ion-list>
-  </ion-item>
-  <ion-item *ngFor="let item of tradeItems">
-    <ion-list>
-      <!-- 显示回复内容 -->
-      <p>对话内容:</p>
-      <div *ngIf="item.replies && item.replies.length > 0">
-        <ul>
-          <li *ngFor="let reply of item.replies">
-            <p>{{reply.floor}} 楼 {{reply.sender}}: {{reply.content}}</p>
-          </li>
-        </ul>
-      </div>
-    </ion-list>
-  </ion-item>
-  <ion-list>
-    <!-- 回复框 -->
-    <ion-item *ngFor="let item of tradeItems">
-      <ion-item>
-        <ion-textarea placeholder="输入回复内容" [(ngModel)]="replyContent"></ion-textarea>
-      </ion-item>
-      <ion-button (click)="submitReply(item)">提交回复</ion-button>
-    </ion-item>
-  </ion-list>
-</ion-content>

+ 0 - 6
src/app/tab4/tab12/tab12.page.scss

@@ -1,6 +0,0 @@
-ion-button {
-    position: absolute;
-    top: 0;
-    right: 0;
-    margin: 10px;
-}

+ 0 - 42
src/app/tab4/tab12/tab12.page.ts

@@ -1,42 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { DataService } from '../data.service';
-import { Title } from '@angular/platform-browser';
-
-@Component({
-  selector: 'app-tab12',
-  templateUrl: './tab12.page.html',
-  styleUrls: ['./tab12.page.scss'],
-})
-export class Tab12Page implements OnInit {
-
-  replyContent: string = " ";
-  tradeItems: any[] = [];
-
-  constructor(private dataService: DataService,private titleService: Title) {}
-
-  ngOnInit() {
-    this.tradeItems = this.dataService.getTradeItems();
-    // 假设helpItems是从DataService中获取的求助信息数组,确保数据已经被加载
-    if (this.tradeItems.length > 0) {
-      this.titleService.setTitle(this.tradeItems[0].title);
-    }
-  }
-
-  submitReply(item: any) {
-    if (this.replyContent.trim() !== '') {
-      const reply = { content: this.replyContent, sender: '小序', floor: 1 }; // 可以根据实际情况设置回复人,初始化楼层号
-      this.dataService.addReplyToTrade(item.id, reply);
-      this.replyContent = ''; // 清空回复框
-    }
-  }
-
-  onFileChange(event: any, item: any) {
-    const file = event.target.files[0];
-    const reader = new FileReader();
-    reader.onload = (e: any) => {
-      item.image = e.target.result;
-    };
-    reader.readAsDataURL(file);
-  }
-
-}

+ 0 - 22
src/app/tab4/tab13/tab13-routing.module.ts

@@ -1,22 +0,0 @@
-import { NgModule } from '@angular/core';
-import { Routes, RouterModule } from '@angular/router';
-
-import { Tab13Page } from './tab13.page';
-
-const newLocal = './tab1/tab13/tab13.module';
-const routes: Routes = [
-  {
-    path: '',
-    component: Tab13Page
-  },
-  {
-    path: 'tab13/:id',
-    loadChildren: () => import(newLocal).then( m => m.Tab13PageModule)
-  },
-];
-
-@NgModule({
-  imports: [RouterModule.forChild(routes)],
-  exports: [RouterModule],
-})
-export class Tab13PageRoutingModule {}

+ 0 - 63
src/app/tab4/tab13/tab13.page.html

@@ -1,63 +0,0 @@
-<ion-header [translucent]="true">
-  <ion-toolbar>
-    <ion-title>
-      <ion-grid>
-        <ion-row>
-          <ion-col size="2">
-            <ion-avatar>
-              <ion-img *ngIf="lostFoundItems.length > 0" [src]="lostFoundItems[0].headImage" class="avatar"></ion-img>
-              <ion-icon *ngIf="lostFoundItems.length === 0" name="person-circle-outline" class="default-avatar"></ion-icon>
-            </ion-avatar>
-          </ion-col>
-          <ion-col size="5">
-            <ion-label>
-              <ion-note>{{lostFoundItems.length > 0 ? lostFoundItems[0].publisher : 'tab11'}}</ion-note>
-              <p>发布时间: {{lostFoundItems.length > 0 ? lostFoundItems[0].timestamp : 'N/A'}}</p>
-            </ion-label>
-          </ion-col>
-        </ion-row>
-        <ion-row>
-          <ion-col size="12">
-            <h2>{{lostFoundItems.length > 0 ? lostFoundItems[0].title : 'tab13'}}</h2>
-          </ion-col>
-        </ion-row>
-      </ion-grid>
-    </ion-title>
-  </ion-toolbar>
-</ion-header>
-
-<ion-content>
-  <ion-item *ngFor="let item of lostFoundItems">
-    <ion-list>
-      <p>物品: {{item.item}}</p>
-      <p>丢失/拾取: {{item.status}}</p>
-      <p>地点: {{item.location}}</p>
-      <p>时间: {{item.time}}</p>
-      <p>图片:</p>
-         <p><img *ngIf="item.image" [src]="item.image" style="max-width: 100px;"></p>
-      <p>备注: {{item.remark}}</p>
-    </ion-list>
-  </ion-item>
-  <ion-item *ngFor="let item of lostFoundItems">
-    <ion-list>
-      <!-- 显示回复内容 -->
-        <p>对话内容:</p>
-      <div *ngIf="item.replies && item.replies.length > 0">
-        <ul>
-          <li *ngFor="let reply of item.replies">
-            <p>{{reply.floor}} 楼 {{reply.sender}}: {{reply.content}}</p>
-          </li>
-        </ul>
-      </div>
-    </ion-list>
-  </ion-item>
-  <ion-list>
-    <!-- 回复框 -->
-    <ion-item *ngFor="let item of lostFoundItems">
-      <ion-item>
-        <ion-textarea placeholder="输入回复内容" [(ngModel)]="replyContent"></ion-textarea>
-      </ion-item>
-      <ion-button (click)="submitReply(item)">提交回复</ion-button>
-    </ion-item>
-  </ion-list>
-</ion-content>

+ 0 - 6
src/app/tab4/tab13/tab13.page.scss

@@ -1,6 +0,0 @@
-ion-button {
-    position: absolute;
-    top: 0;
-    right: 0;
-    margin: 10px;
-}

+ 0 - 42
src/app/tab4/tab13/tab13.page.ts

@@ -1,42 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { DataService } from '../data.service';
-import { Title } from '@angular/platform-browser';
-
-@Component({
-  selector: 'app-tab13',
-  templateUrl: './tab13.page.html',
-  styleUrls: ['./tab13.page.scss'],
-})
-export class Tab13Page implements OnInit {
-
-  replyContent: string = " ";
-  lostFoundItems: any[] = [];
-
-  constructor(private dataService: DataService,private titleService: Title) {}
-
-  ngOnInit() {
-    this.lostFoundItems = this.dataService.getLostFoundItems();
-    // 假设helpItems是从DataService中获取的求助信息数组,确保数据已经被加载
-    if (this.lostFoundItems.length > 0) {
-      this.titleService.setTitle(this.lostFoundItems[0].title);
-    }
-  }
-
-  submitReply(item: any) {
-    if (this.replyContent.trim() !== '') {
-      const reply = { content: this.replyContent, sender:"小序", floor: "" }; // 可以根据实际情况设置回复人,初始化楼层号
-      this.dataService.addReplyToLostFoundItems(item.id, reply);
-      this.replyContent = ''; // 清空回复框
-    }
-  }
-
-  onFileChange(event: any, item: any) {
-    const file = event.target.files[0];
-    const reader = new FileReader();
-    reader.onload = (e: any) => {
-      item.image = e.target.result;
-    };
-    reader.readAsDataURL(file);
-  }
-
-}

+ 0 - 22
src/app/tab4/tab14/tab14-routing.module.ts

@@ -1,22 +0,0 @@
-import { NgModule } from '@angular/core';
-import { Routes, RouterModule } from '@angular/router';
-
-import { Tab14Page } from './tab14.page';
-
-const newLocal = './tab1/tab14/tab14.module';
-const routes: Routes = [
-  {
-    path: '',
-    component: Tab14Page
-  },
-  {
-    path: 'tab14/:id',
-    loadChildren: () => import(newLocal).then( m => m.Tab14PageModule)
-  },
-];
-
-@NgModule({
-  imports: [RouterModule.forChild(routes)],
-  exports: [RouterModule],
-})
-export class Tab14PageRoutingModule {}

+ 0 - 62
src/app/tab4/tab14/tab14.page.html

@@ -1,62 +0,0 @@
-<ion-header [translucent]="true">
-  <ion-toolbar>
-    <ion-title>
-      <ion-grid>
-        <ion-row>
-          <ion-col size="2">
-            <ion-avatar>
-              <ion-img *ngIf="activityItems.length > 0" [src]="activityItems[0].headImage" class="avatar"></ion-img>
-              <ion-icon *ngIf="activityItems.length === 0" name="person-circle-outline" class="default-avatar"></ion-icon>
-            </ion-avatar>
-          </ion-col>
-          <ion-col size="5">
-            <ion-label>
-              <ion-note>{{activityItems.length > 0 ? activityItems[0].publisher : 'tab11'}}</ion-note>
-              <p>发布时间: {{activityItems.length > 0 ? activityItems[0].timestamp : 'N/A'}}</p>
-            </ion-label>
-          </ion-col>
-        </ion-row>
-        <ion-row>
-          <ion-col size="12">
-            <h2>{{activityItems.length > 0 ? activityItems[0].title : 'tab14'}}</h2>
-          </ion-col>
-        </ion-row>
-      </ion-grid>
-    </ion-title>
-  </ion-toolbar>
-</ion-header>
-
-<ion-content>
-  <ion-item *ngFor="let item of activityItems">
-    <ion-list>
-      <p>活动名称: {{item.name}}</p>
-      <p>可参与对象: {{item.target}}</p>
-      <p>开始时间: {{item.startTime}}</p>
-      <p>持续时长: {{item.duration}}</p>
-      <p>地点: {{item.location}}</p>
-      <p>备注: {{item.remark}}</p>
-    </ion-list>
-  </ion-item>
-  <ion-item *ngFor="let item of activityItems">
-    <ion-list>
-      <!-- 显示回复内容 -->
-      <p>对话内容:</p>
-      <div *ngIf="item.replies && item.replies.length > 0">
-        <ul>
-          <li *ngFor="let reply of item.replies">
-            <p>{{reply.floor}} 楼 {{reply.sender}}: {{reply.content}}</p>
-          </li>
-        </ul>
-      </div>
-    </ion-list>
-  </ion-item>
-  <ion-list>
-    <!-- 回复框 -->
-    <ion-item *ngFor="let item of activityItems">
-      <ion-item>
-        <ion-textarea placeholder="输入回复内容" [(ngModel)]="replyContent"></ion-textarea>
-      </ion-item>
-      <ion-button class="sender" (click)="submitReply(item)">提交回复</ion-button>
-    </ion-item>
-  </ion-list>
-</ion-content>

+ 0 - 6
src/app/tab4/tab14/tab14.page.scss

@@ -1,6 +0,0 @@
-ion-button {
-    position: absolute;
-    top: 0;
-    right: 0;
-    margin: 10px;
-}

部分文件因为文件数量过多而无法显示