基于微信小程序开发的宠物领养平台——代码解读

152 阅读12分钟

写在前面:

我手里有个长期项目,考虑接私活的可以看看我GitHub!

github.com/ccy-233/cod…

项目前端

一、项目的技术架构概况

一句话概括:该项目是基于微信小程序开发的宠物领养平台,采用原生小程序框架进行用户界面的构建,使用 wx.request 进行 API 请求,并通过 getApp() 和本地存储来管理全局状态和用户信息。

一)技术栈主要包括

  • 微信小程序框架:项目的核心框架,用于构建用户界面和实现交互逻辑。
  • JavaScript:主要编程语言,项目中的所有逻辑均使用 JavaScript 实现。
  • JSON:用于配置页面和组件的元信息,如页面路径、组件属性等。
  • WXML:类似 HTML 的模板语言,用于定义页面结构。
  • WXSS:类似 CSS 的样式表语言,用于定义页面和组件的样式。
  • 本地存储:通过 wx.setStorageSyncwx.getStorageSync 管理用户信息和 Token。
  • 网络请求:使用 wx.request 进行 API 请求,与后端进行数据交互。

二)开发环境要求

  • 微信开发者工具:用于开发和调试微信小程序。
  • IDE:推荐使用 IDEA 或 VS Code,配合微信开发者工具进行开发。
  • 依赖管理:通过微信开发者工具自带的依赖管理功能安装和管理项目依赖。
  • 浏览器:Edge 或 Chrome,主要用于调试和查看控制台输出。

三)项目结构分析

└── pet_adoption
    ├── backend
    │   ├── src
    │   │   └── main
    │   │       ├── java
    │   │       ├── resources
    │   │       └── webapp
    │   ├── adopt.sql
    │   └── pom.xml
    └── mp-weixin
        ├── components
        │   └── comment
        ├── pages
        │   ├── adopt
        │   ├── blog
        │   ├── comment
        │   ├── index
        │   ├── message
        │   ├── pet
        │   └── user
        ├── static
        │   └── images
        ├── subpackages
        │   ├── comment
        │   └── guide
        ├── utils
        │   └── imagePathUtils.js
        ├── app.js
        ├── app.json
        ├── config.js
        ├── project.config.json
        └── project.private.config.json
关键目录说明
  • mp-weixin/:微信小程序的主要目录,包含所有前端代码。
    • components/:存放可复用的组件,如评论组件。
    • pages/:存放各个页面的代码,每个页面有对应的 .js.json.wxml.wxss 文件。
    • static/:存放静态资源,如图片。
    • subpackages/:存放子包,用于分包加载优化性能。
    • utils/:存放工具函数,如图片路径处理工具。
    • app.js:应用的入口文件,包含全局状态管理和生命周期方法。
    • app.json:应用的全局配置文件,定义了页面路径和窗口表现。
    • config.js:配置文件,定义了不同环境下的 API 地址。

四)UI组件设计

1. 布局组件
  • TabBar:项目中使用了 TabBar 进行页面导航,定义在 app.json 中,包含首页、宠物列表和个人中心三个主要页面。
  • Swiper:用于实现轮播图组件,如首页的轮播图展示。
  • Navigator:用于页面之间的跳转,常见于各个页面的跳转按钮。
2. 业务组件
  • Form 组件:如 adopt/apply.js 中的领养申请表单,实现了表单验证和数据提交功能。
  • List 组件:如 adopt/list.js 中的领养申请列表,支持分页加载、筛选和刷新功能。
  • Modal 组件:用于显示确认对话框,如领养申请的审核通过和拒绝操作。
  • Image 组件:处理图片路径和预览功能,如 blog/publish.js 中的图片上传和预览。
3. 数据流转
  • Props 和 Events:小程序中通过 data 属性传递数据,通过 bind 方法绑定事件,实现父子组件之间的通信。
  • 全局状态管理:通过 getApp() 获取全局应用实例,使用 globalData 管理全局状态,如用户信息、Token 和未读消息数。
  • 本地存储:使用 wx.setStorageSyncwx.getStorageSync 存储和读取用户信息和 Token,确保用户登录状态的持久化。
  • API 请求:通过 wx.request 发起网络请求,与后端进行数据交互,请求结果通过 Promise 进行处理,确保异步操作的可控性。

五)交互实现

1. 登录与鉴权
  • 登录页面 (user/login.js):提供了微信登录和账号密码登录两种方式,登录成功后保存用户信息和 Token 到本地存储和全局状态中。
  • 鉴权拦截:多个页面在 onLoadonShow 生命周期方法中检查用户是否已登录,若未登录则提示用户登录。
2. 数据加载与刷新
  • 下拉刷新:多个页面实现了下拉刷新功能,如 adopt/list.jsuser/favorite.js,通过 onPullDownRefresh 方法触发数据重新加载。
  • 上拉加载更多:如 adopt/list.jsuser/favorite.js 中的 onReachBottom 方法,用于分页加载更多数据。
  • 轮询机制:如 message/chat.js 中的 startPolling 方法,通过定时器定期检查新消息。
3. 图片处理
  • 图片上传:如 blog/publish.jsblog/edit.js 中的 uploadImages 方法,实现了图片的上传和路径处理。
  • 图片预览:多个页面实现了图片预览功能,如 blog/publish.js 中的 previewImage 方法。
4. 表单验证
  • 表单验证:如 adopt/apply.js 中的 validateForm 方法,对表单数据进行验证,确保提交数据的有效性。
5. 路由管理
  • 页面跳转:使用 wx.navigateTowx.switchTab 实现页面间的跳转,如 index/index.js 中的 navigateToPetDetailnavigateToBlogs 方法。

以上是对项目前端部分的详细分析,涵盖了技术架构、组件设计和交互逻辑等方面的内容。该项目采用了原生微信小程序框架,结合了多种交互方式和状态管理手段,确保了良好的用户体验和高效的开发流程。

二、主要业务功能在代码中的具体实现 & 调用过程

有以下核心业务功能:

  1. 用户认证与权限管理 - 处理用户登录、权限验证和身份管理。
  2. 宠物领养管理 - 包括领养申请、申请详情、申请管理等。
  3. 宠物信息管理 - 允许用户查看、发布、编辑和删除宠物信息。
  4. 故事管理 - 包括发布故事、编辑故事等。
  5. 消息管理 - 用户之间的消息交互。
  6. 收藏管理 - 用户可以收藏宠物和博客。
  7. 用户信息管理 - 用户可以查看和编辑个人信息。

1. 用户认证与权限管理

设计思路

  • 用户通过登录页面提交认证信息。
  • 系统后端验证用户信息,并根据用户角色(普通用户、管理员)提供相应权限。
  • 用户可以通过微信登录或账号密码登录。

代码实现逻辑

  • 用户填写用户名和密码,选择用户类型后提交表单。
  • 后端接收请求,校验用户名和密码的正确性。
  • 登录成功后,将用户信息和token存储到本地,并更新全局数据。
  • 微信登录通过获取code,发送到后端验证,登录成功后同样存储用户信息和token。

调用过程

  • 用户填写登录信息 -> 提交表单 -> 调用后端登录接口 -> 验证用户信息 -> 返回登录结果 -> 存储用户信息和token -> 更新全局数据。

代码片段 (user/login.js 中的登录部分):

async onLogin() {
  if (this.data.loading) return;
  if (!this.data.isAgree) {
    this.setData({ errorMsg: '请先同意用户协议和隐私政策' });
    return;
  }
  if (!this.data.username || !this.data.password) {
    this.setData({ errorMsg: '请输入用户名和密码' });
    return;
  }
  this.setData({ loading: true, errorMsg: '' });
  try {
    const res = await wx.request({
      url: `${app.globalData.baseUrl}/user/login`,
      method: 'POST',
      data: { userName: this.data.username, password: this.data.password }
    });
    if (res.statusCode === 200 && res.data.code === 200) {
      const userData = res.data.data;
      wx.setStorageSync('userInfo', userData.user);
      wx.setStorageSync('token', userData.token);
      wx.setStorageSync('isLogin', true);
      wx.setStorageSync('userId', userData.user.id);
      app.globalData.userInfo = userData.user;
      app.globalData.token = userData.token;
      app.globalData.userId = userData.user.id;
      wx.showToast({ title: '登录成功', icon: 'success' });
      setTimeout(() => {
        const pages = getCurrentPages();
        if (pages.length > 1) {
          wx.navigateBack();
        } else {
          wx.switchTab({ url: '/pages/index/index' });
        }
      }, 1500);
    } else {
      this.setData({ errorMsg: res.data.message || '登录失败' });
    }
  } catch (error) {
    this.setData({ errorMsg: error.errMsg || '登录失败,请重试' });
  } finally {
    this.setData({ loading: false });
  }
}

2. 宠物领养管理

设计思路

  • 用户可以申请领养宠物,填写相关信息。
  • 管理员或宠物发布者可以审核领养申请。
  • 用户可以查看领养申请的详细信息。

代码实现逻辑

  • 用户填写领养申请表单,提交后发送至后端。
  • 后端验证表单信息,并存储申请记录。
  • 管理员或宠物发布者可以在列表中查看申请,进行审核操作。
  • 审核通过或拒绝后,更新申请状态,并通知相关用户。

调用过程

  • 用户填写领养申请表单 -> 提交表单 -> 调用后端接口 -> 存储申请记录 -> 管理员或宠物发布者查看申请 -> 审核通过或拒绝 -> 更新申请状态。

代码片段 (adopt/apply.js 中的表单提交部分):

submitForm(e) {
  const formData = e.detail.value;
  if (!this.validateForm(formData)) return;
  this.setData({ submitting: true });
  const requestData = {
    petId: this.data.petId,
    remark: JSON.stringify({
      name: formData.name,
      phone: formData.phone,
      wechat: formData.wechat,
      address: formData.address,
      houseType: this.data.houseTypes[formData.houseTypeIndex],
      isRent: formData.isRent,
      landlordAgree: formData.landlordAgree,
      familyMembers: formData.familyMembers,
      familyAgree: formData.familyAgree,
      hasPetExperience: formData.hasPetExperience,
      description: formData.description
    })
  };
  wx.request({
    url: `${app.globalData.baseUrl}/adopt/apply`,
    method: 'POST',
    header: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${wx.getStorageSync('token')}`
    },
    data: requestData,
    success: (res) => {
      if (res.data.code === 200) {
        wx.showToast({ title: '申请提交成功', icon: 'success' });
        setTimeout(() => {
          const pages = getCurrentPages();
          const prevPage = pages[pages.length - 2];
          if (prevPage && prevPage.loadPetDetail) {
            prevPage.loadPetDetail(prevPage.data.pet.id);
          }
          wx.navigateBack();
        }, 2000);
      } else {
        wx.showToast({ title: res.data.message || '提交失败,请重试', icon: 'error' });
      }
    },
    complete: () => {
      this.setData({ submitting: false });
    }
  });
}

3. 宠物信息管理

设计思路

  • 用户可以查看宠物列表、发布新宠物、编辑已有的宠物信息。
  • 宠物信息包括图片、描述等。

代码实现逻辑

  • 前端展示宠物列表,用户可以选择查看、发布或编辑宠物。
  • 发布和编辑宠物时,用户可以上传图片并填写相关信息。
  • 提交后,后端验证并存储宠物信息。

调用过程

  • 用户查看宠物列表 -> 点击发布或编辑 -> 填写宠物信息 -> 提交表单 -> 调用后端接口 -> 存储宠物信息。

代码片段 (pet/publish.js 中的宠物发布部分):

async submitForm(e) {
  try {
    const { title, content } = this.data.formData;
    if (!title.trim()) {
      wx.showToast({ title: '请输入故事标题', icon: 'none' });
      return;
    }
    if (!content.trim()) {
      wx.showToast({ title: '请输入故事内容', icon: 'none' });
      return;
    }
    wx.showLoading({ title: '保存中...', mask: true });
    const images = await this.uploadImages();
    const coverImage = images.join(',');
    const petData = {
      title: title.trim(),
      content: content.trim(),
      coverImage,
      tags: this.data.selectedTags.join(',')
    };
    const res = await wx.request({
      url: `${app.globalData.baseUrl}/pets`,
      method: 'POST',
      data: petData,
      header: {
        'Authorization': wx.getStorageSync('token'),
        'Content-Type': 'application/json'
      }
    });
    if (res.statusCode === 200 && res.data.code === 200) {
      wx.hideLoading();
      wx.showToast({ title: '保存成功', icon: 'success' });
      setTimeout(() => {
        const pages = getCurrentPages();
        const detailPage = pages[pages.length - 2];
        if (detailPage && detailPage.loadPetDetail) {
          detailPage.loadPetDetail();
        }
        wx.navigateBack();
      }, 1500);
    } else {
      throw new Error(res.data.message || '保存失败');
    }
  } catch (error) {
    wx.hideLoading();
    wx.showToast({ title: error.message || '保存失败', icon: 'none' });
  }
}

4. 故事管理

设计思路

  • 用户可以发布新的故事,编辑已有的故事。
  • 故事包括标题、内容、封面图片和标签。

代码实现逻辑

  • 用户填写故事标题、内容、选择封面图片和标签。
  • 提交后,后端验证并存储故事信息。

调用过程

  • 用户填写故事信息 -> 提交表单 -> 调用后端接口 -> 存储故事信息。

代码片段 (blog/publish.js 中的故事发布部分):

submitForm(e) {
  try {
    const { title, content } = this.data.formData;
    if (!title.trim()) {
      wx.showToast({ title: '请输入故事标题', icon: 'none' });
      return;
    }
    if (!content.trim()) {
      wx.showToast({ title: '请输入故事内容', icon: 'none' });
      return;
    }
    if (this.data.tempImages.length === 0) {
      wx.showToast({ title: '请至少上传一张图片', icon: 'none' });
      return;
    }
    wx.showLoading({ title: '正在发布...', mask: true });
    this.uploadImages()
      .then(uploadedImages => {
        const submitData = {
          title: title.trim(),
          content: content.trim(),
          coverImage: uploadedImages.join(','),
          tags: this.data.selectedTags.join(','),
          status: 1
        };
        return wx.request({
          url: `${app.globalData.baseUrl}/blogs`,
          method: 'POST',
          data: submitData,
          header: {
            'Authorization': `Bearer ${wx.getStorageSync('token').replace('Bearer ', '')}`,
            'Content-Type': 'application/json'
          }
        });
      })
      .then(res => {
        if (res.statusCode === 200 && res.data.code === 200) {
          wx.hideLoading();
          wx.showToast({ title: '发布成功', icon: 'success', duration: 2000 });
          setTimeout(() => {
            wx.navigateBack();
          }, 2000);
        } else {
          throw new Error(res.data.message || '发布失败');
        }
      })
      .catch(err => {
        wx.hideLoading();
        wx.showToast({ title: typeof err === 'string' ? err : (err.message || '发布失败'), icon: 'none', duration: 2000 });
      });
  } catch (error) {
    wx.hideLoading();
    wx.showToast({ title: error.message || '发布失败', icon: 'none', duration: 2000 });
  }
}

5. 消息管理

设计思路

  • 用户之间可以进行消息交流。
  • 消息包括文本、图片等内容。

代码实现逻辑

  • 用户发送消息,消息存储到数据库。
  • 消息列表页面展示用户之间的消息,并支持加载更多。
  • 消息实时轮询,保证用户及时看到新消息。

调用过程

  • 用户输入消息内容 -> 点击发送 -> 调用后端接口 -> 存储消息 -> 更新消息列表 -> 实时轮询新消息。

代码片段 (message/chat.js 中的消息发送部分):

async sendMessage() {
  const content = this.data.inputContent.trim();
  if (!content) return;
  const token = wx.getStorageSync('token');
  if (!token) return;
  try {
    const requestData = {
      sessionId: this.data.sessionId,
      senderId: this.data.userInfo.id,
      content
    };
    const res = await wx.request({
      url: `${app.globalData.baseUrl}/messages/send`,
      method: 'POST',
      header: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
      data: requestData
    });
    if (res.statusCode === 200 && res.data.code === 200) {
      this.setData({ inputContent: '' });
      const newMessage = res.data.data;
      newMessage.createTimeStr = this.formatTime(newMessage.createTime);
      newMessage.senderAvatar = newMessage.sender?.pic ? getImageUrl(newMessage.sender.pic) : '/static/images/icon/default-avatar.png';
      const messages = [...this.data.messages, newMessage];
      this.setData({ messages, scrollIntoView: `msg-${newMessage.id}` });
    } else {
      wx.showToast({ title: res.data.message || '发送失败', icon: 'none' });
    }
  } catch (error) {
    wx.showToast({ title: error.message || '网络错误', icon: 'none' });
  }
}

6. 收藏管理

设计思路

  • 用户可以收藏感兴趣的宠物和博客。
  • 用户可以查看已收藏的宠物和博客,并进行取消收藏操作。

代码实现逻辑

  • 用户点击收藏按钮,后端记录收藏信息。
  • 用户可以在收藏页面查看已收藏的内容,并进行取消收藏操作。

调用过程

  • 用户点击收藏按钮 -> 调用后端接口 -> 记录收藏信息 -> 用户查看收藏列表 -> 取消收藏 -> 调用取消收藏接口。

代码片段 (user/favorite.js 中的收藏部分):

async onFavoriteTap(e) {
  const { id, type } = e.currentTarget.dataset;
  const token = wx.getStorageSync('token');
  if (!token) return;
  try {
    const url = type === 'pet' ? `${app.globalData.baseUrl}/favorites/remove/${id}` : `${app.globalData.baseUrl}/blog/favorites/remove`;
    const data = type === 'pet' ? null : { blogId: id };
    const method = 'POST';
    const res = await wx.request({
      url,
      method,
      header: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
      data
    });
    if (res.data.code === 200) {
      const listKey = type === 'pet' ? 'pets' : 'blogs';
      const list = this.data[listKey].filter(item => item.id !== id);
      this.setData({ [listKey]: list });
      wx.showToast({ title: '已取消收藏', icon: 'success' });
    } else {
      throw new Error(res.data.message || '操作失败');
    }
  } catch (error) {
    wx.showToast({ title: error.message || '操作失败', icon: 'none' });
  }
}

7. 用户信息管理

设计思路

  • 用户可以查看和编辑个人信息。
  • 用户可以上传头像、修改昵称、真实姓名、电话号码等。

代码实现逻辑

  • 用户选择头像或填写个人信息。
  • 提交后,后端验证并更新用户信息。

调用过程

  • 用户选择头像或填写信息 -> 提交表单 -> 调用后端接口 -> 更新用户信息。

代码片段 (user/profile.js 中的用户信息保存部分):

async onSave() {
  try {
    if (this.data.userInfo.telephone && !/^1[3-9]\d{9}$/.test(this.data.userInfo.telephone)) {
      wx.showToast({ title: '手机号码格式不正确', icon: 'none' });
      return;
    }
    if (this.data.userInfo.email && !/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/.test(this.data.userInfo.email)) {
      wx.showToast({ title: '邮箱格式不正确', icon: 'none' });
      return;
    }
    wx.showLoading({ title: '保存中...', mask: true });
    const updateData = {
      id: this.data.userInfo.id,
      ...this.data.userInfo
    };
    const res = await wx.request({
      url: `${app.globalData.baseUrl}/user/update`,
      method: 'POST',
      data: updateData,
      header: {
        'Authorization': `Bearer ${wx.getStorageSync('token')}`,
        'Content-Type': 'application/json'
      }
    });
    if (res.data.code === 200) {
      wx.setStorageSync('userInfo', this.data.userInfo);
      app.globalData.userInfo = this.data.userInfo;
      wx.showToast({ title: '保存成功', icon: 'success' });
      setTimeout(() => {
        wx.navigateBack();
      }, 1500);
    } else {
      throw new Error(res.data.message || '保存失败');
    }
  } catch (error) {
    wx.showToast({ title: error.message || '保存失败', icon: 'none' });
  } finally {
    wx.hideLoading();
  }
}

8. 首页展示与导航

设计思路

  • 首页展示轮播图、推荐宠物和最新故事,吸引用户关注。
  • 提供导航功能,用户可以跳转到不同的页面。

代码实现逻辑

  • 加载轮播图、推荐宠物和最新故事的数据。
  • 处理图片路径,确保显示正确的图片。
  • 提供跳转功能,如跳转到宠物详情、博客详情、发布宠物等。

调用过程

  • 页面加载 -> 初始化数据 -> 加载轮播图、推荐宠物和最新故事 -> 展示数据 -> 用户点击跳转 -> 导航到对应页面。

代码片段 (index/index.js 中的首页加载部分):

initData: function() {
  this.loadBanners();
  this.loadRecommendPets();
  this.loadLatestBlogs();
}

loadBanners: function() {
  const that = this;
  wx.request({
    url: `${getApp().globalData.baseUrl}/banners`,
    success: function(res) {
      if (res.data.code === 200) {
        const banners = res.data.data.map(banner => {
          let imageUrl = banner.imageUrl;
          if (imageUrl && imageUrl.includes('localhost')) {
            const serverUrl = config.baseUrl.match(/^(https?:\/\/[^\/]+)/)[0];
            imageUrl = imageUrl.replace(/http:\/\/localhost:8080/, serverUrl);
          }
          return { ...banner, imageUrl };
        });
        that.setData({ bannerList: banners });
      }
    },
    complete: function() {
      that.setData({ loading: false });
    }
  });
}

9. 忘记密码

设计思路

  • 用户可以通过手机号找回密码。
  • 系统验证用户信息并重置密码。

代码实现逻辑

  • 用户输入用户名、手机号和新密码。
  • 系统验证手机号格式和密码一致性。
  • 发送重置密码请求到后端。

调用过程

  • 用户输入信息 -> 提交表单 -> 验证信息 -> 调用后端接口 -> 重置密码 -> 返回结果。

代码片段 (user/forgot-password.js 中的重置密码部分):

async onResetPassword() {
  if (this.data.loading) return;
  if (!this.data.userName || !this.data.telephone) {
    this.setData({ errorMsg: '请输入用户名和手机号' });
    return;
  }
  if (!/^1[3-9]\d{9}$/.test(this.data.telephone)) {
    this.setData({ errorMsg: '请输入正确的手机号' });
    return;
  }
  if (!this.data.password || !this.data.confirmPassword) {
    this.setData({ errorMsg: '请输入新密码' });
    return;
  }
  if (this.data.password !== this.data.confirmPassword) {
    this.setData({ errorMsg: '两次输入的密码不一致' });
    return;
  }
  if (this.data.password.length < 6) {
    this.setData({ errorMsg: '密码长度不能少于6位' });
    return;
  }
  this.setData({ loading: true, errorMsg: '' });
  try {
    const res = await wx.request({
      url: `${app.globalData.baseUrl}/user/reset-password`,
      method: 'POST',
      header: { 'Content-Type': 'application/json' },
      data: { userName: this.data.userName, telephone: this.data.telephone, password: this.data.password }
    });
    if (res.statusCode === 200 && res.data.code === 200) {
      wx.showToast({ title: '密码重置成功', icon: 'success' });
      setTimeout(() => {
        wx.navigateBack();
      }, 1500);
    } else {
      throw new Error(res.data.message || '未知错误');
    }
  } catch (error) {
    this.setData({ errorMsg: error.message || '重置密码失败,请重试' });
  } finally {
    this.setData({ loading: false });
  }
}

10. 用户设置

设计思路

  • 用户可以修改通知设置、切换深色模式、修改密码、清除缓存和退出登录。
  • 提供关于我们的页面链接。

代码实现逻辑

  • 用户选择设置项,如修改通知设置、切换深色模式等。
  • 修改密码时,用户输入旧密码和新密码,后端验证并更新密码。
  • 清除缓存时,删除本地存储的所有数据。
  • 退出登录时,清除用户信息并返回登录页。

调用过程

  • 用户选择设置项 -> 修改设置 -> 更新本地存储或调用后端接口 -> 返回结果。

代码片段 (user/settings.js 中的修改密码部分):

changePassword: function() {
  wx.navigateTo({
    url: '/pages/user/change-password'
  });
}

logout: function() {
  const that = this;
  wx.showModal({
    title: '提示',
    content: '确定要退出登录吗?',
    success: function(res) {
      if (res.confirm) {
        wx.request({
          url: `${getApp().globalData.baseUrl}/user/logout`,
          method: 'POST',
          header: { 'Authorization': wx.getStorageSync('token') },
          success: function(res) {
            if (res.data.code === 0) {
              wx.removeStorageSync('userInfo');
              wx.removeStorageSync('token');
              that.setData({ userInfo: null });
              wx.showToast({ title: '已退出登录', icon: 'success' });
              setTimeout(() => {
                wx.reLaunch({ url: '/pages/user/index' });
              }, 1500);
            } else {
              wx.showToast({ title: res.data.message || '退出失败', icon: 'none' });
            }
          },
          fail: function() {
            wx.showToast({ title: '网络错误', icon: 'none' });
          }
        });
      }
    }
  });
}

11. 宠物详情与编辑

设计思路

  • 用户可以查看宠物的详细信息,包括图片、描述等。
  • 用户可以编辑已发布的宠物信息。

代码实现逻辑

  • 加载宠物详情数据,处理图片路径。
  • 编辑宠物时,用户可以修改图片、描述等信息。
  • 提交编辑内容,后端验证并更新宠物信息。

调用过程

  • 用户查看宠物详情 -> 加载数据 -> 用户点击编辑 -> 修改信息 -> 提交表单 -> 更新宠物信息。

代码片段 (pet/detail.js 中的宠物详情加载部分):

loadPetDetail() {
  const that = this;
  wx.request({
    url: `${app.globalData.baseUrl}/pets/${this.data.petId}`,
    success: function(res) {
      if (res.statusCode === 200 && res.data.code === 200) {
        const pet = res.data.data;
        let images = [];
        let mainImage = '/static/images/pets/default.jpg';
        if (pet.pic) {
          images = pet.pic.split(',').map(pic => {
            pic = pic.trim();
            if (!pic.startsWith('http') && !pic.startsWith('https')) {
              if (pic.startsWith('/adopt')) {
                pic = pic.substring(6);
              }
              if (!pic.startsWith('/')) {
                pic = '/' + pic;
              }
              return `${app.globalData.baseUrl}${pic}`;
            }
            return pic;
          });
          mainImage = images[0];
        }
        that.setData({ pet, images, mainImage });
      } else {
        wx.showToast({ title: res.data.message || '加载失败', icon: 'none' });
      }
    }
  });
}

12. 领养申请管理

设计思路

  • 用户可以查看和管理领养申请,包括通过和拒绝申请。
  • 管理员可以查看所有领养申请,并进行批量审核。

代码实现逻辑

  • 加载领养申请列表,处理图片路径。
  • 管理员或宠物发布者可以审核申请,填写拒绝理由。
  • 审核通过或拒绝后,更新申请状态,并通知相关用户。

调用过程

  • 用户查看领养申请列表 -> 加载数据 -> 用户点击审核 -> 填写拒绝理由 -> 提交审核 -> 更新申请状态。

代码片段 (adopt/manage.js 中的领养申请审核部分):

async updateAdoptStatus(e) {
  const { id, status } = e.currentTarget.dataset;
  try {
    const res = await wx.request({
      url: `${app.globalData.baseUrl}/adopt/status`,
      method: 'PUT',
      data: { id, status },
      header: { 'Authorization': `Bearer ${wx.getStorageSync('token')}`, 'Content-Type': 'application/json' }
    });
    if (res.data.code === 200) {
      wx.showToast({ title: '更新成功', icon: 'success' });
      this.loadAdoptions(true);
    } else {
      throw new Error(res.data.message || '更新失败');
    }
  } catch (error) {
    wx.showToast({ title: error.message || '更新失败', icon: 'none' });
  }
}

13. 用户收藏管理

设计思路

  • 用户可以收藏宠物和博客。
  • 用户可以在收藏页面查看和取消收藏。

代码实现逻辑

  • 加载用户收藏的宠物和博客列表。
  • 用户点击取消收藏按钮,发送请求到后端。
  • 成功取消收藏后,更新本地收藏列表。

调用过程

  • 用户进入收藏页面 -> 加载收藏列表 -> 用户点击取消收藏 -> 发送请求 -> 更新收藏列表。

代码片段 (user/favorite.js 中的取消收藏部分):

async onFavoriteTap(e) {
  const { id, type } = e.currentTarget.dataset;
  const token = wx.getStorageSync('token');
  if (!token) return;
  try {
    const url = type === 'pet' ? `${app.globalData.baseUrl}/favorites/remove/${id}` : `${app.globalData.baseUrl}/blog/favorites/remove`;
    const data = type === 'pet' ? null : { blogId: id };
    const method = 'POST';
    const res = await wx.request({
      url,
      method,
      header: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
      data
    });
    if (res.data.code === 200) {
      const listKey = type === 'pet' ? 'pets' : 'blogs';
      const list = this.data[listKey].filter(item => item.id !== id);
      this.setData({ [listKey]: list });
      wx.showToast({ title: '已取消收藏', icon: 'success' });
    } else {
      throw new Error(res.data.message || '操作失败');
    }
  } catch (error) {
    wx.showToast({ title: error.message || '操作失败', icon: 'none' });
  }
}

14. 用户注册

设计思路

  • 新用户可以通过填写注册信息来创建账户。
  • 注册信息包括用户名、密码、真实姓名、手机号等。

代码实现逻辑

  • 用户填写注册信息,提交表单。
  • 后端验证注册信息的合法性,并创建新用户。
  • 注册成功后,返回登录页。

调用过程

  • 用户填写注册信息 -> 提交表单 -> 验证信息 -> 调用后端接口 -> 创建新用户 -> 返回登录页。

代码片段 (user/register.js 中的注册部分):

async onRegister() {
  if (!this.validateForm()) return;
  try {
    const res = await wx.request({
      url: `${app.globalData.baseUrl}/user/register`,
      method: 'POST',
      data: {
        userName: this.data.username,
        password: this.data.password,
        role: 'USER',
        status: 1,
        ...(this.data.realName ? { realName: this.data.realName } : {}),
        ...(this.data.telephone ? { telephone: this.data.telephone } : {}),
        ...(this.data.email ? { email: this.data.email } : {}),
        ...(this.data.sex ? { sex: this.data.sex } : {}),
        ...(this.data.birthday ? { birthday: this.data.birthday } : {}),
        ...(this.data.description ? { description: this.data.description } : {})
      },
      header: { 'Content-Type': 'application/json' }
    });
    if (res.statusCode === 200 && res.data.code === 200) {
      wx.showToast({ title: '注册成功', icon: 'success' });
      setTimeout(() => {
        wx.navigateBack();
      }, 1500);
    } else {
      throw new Error(res.data.message || '未知错误');
    }
  } catch (error) {
    wx.showToast({ title: error.message || '注册失败,请重试', icon: 'none' });
  } finally {
    wx.hideLoading();
  }
}

15. 用户评论管理

设计思路

  • 用户可以查看和删除自己的评论。
  • 用户可以对宠物或博客发表评论。

代码实现逻辑

  • 加载用户评论列表,处理图片路径。
  • 用户点击删除按钮,发送请求到后端。
  • 成功删除评论后,更新本地评论列表。

调用过程

  • 用户进入评论页面 -> 加载评论列表 -> 用户点击删除 -> 发送请求 -> 更新评论列表。

代码片段 (user/comments.js 中的删除评论部分):

deleteComment: function(e) {
  const commentId = e.currentTarget.dataset.id;
  const that = this;
  wx.showModal({
    title: '提示',
    content: '确定要删除这条评论吗?',
    success: function(res) {
      if (res.confirm) {
        wx.request({
          url: `${getApp().globalData.baseUrl}/comments/${commentId}`,
          method: 'DELETE',
          header: { 'Authorization': wx.getStorageSync('token') },
          success: function(res) {
            if (res.data.code === 0) {
              wx.showToast({ title: '删除成功', icon: 'success' });
              that.setData({ currentPage: 1, hasMore: true, commentList: [] }, () => {
                that.loadComments();
              });
            } else {
              wx.showToast({ title: res.data.message || '删除失败', icon: 'none' });
            }
          }
        });
      }
    }
  });
}

16. 消息轮询与通知

设计思路

  • 实现实时消息轮询,确保用户能及时看到新消息。
  • 消息轮询定时器每30秒检查一次未读消息。

代码实现逻辑

  • 在应用启动和前台显示时,启动消息轮询定时器。
  • 每30秒调用一次未读消息接口,检查是否有新消息。
  • 如果有新消息,更新全局未读消息计数,并显示小红点。

调用过程

  • 应用启动或进入前台 -> 启动消息轮询 -> 定时检查未读消息 -> 更新未读消息计数 -> 显示小红点。

代码片段 (mp-weixin/app.js 中的消息轮询部分):

startCheckingMessages() {
  console.log('开始检查未读消息');
  this.stopCheckingMessages();
  this.checkUnreadMessages();
  this.globalData.messagePollingTimer = setInterval(() => {
    this.checkUnreadMessages();
  }, 30000);
}

async checkUnreadMessages() {
  try {
    const token = this.globalData.token;
    const userInfo = this.globalData.userInfo;
    if (!token || !userInfo) return;
    const res = await wx.request({
      url: `${this.globalData.baseUrl}/messages/unread/count`,
      method: 'GET',
      header: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
      data: { userId: userInfo.id }
    });
    if (res.statusCode === 200 && res.data.code === 200) {
      const unreadCount = res.data.data || 0;
      this.globalData.unreadMessageCount = unreadCount;
      if (unreadCount > 0) {
        wx.showTabBarRedDot({ index: 3 });
      } else {
        wx.hideTabBarRedDot({ index: 3 });
      }
    }
  } catch (error) {
    console.error('检查未读消息失败:', error);
  }
}

17. 图片资源路径管理

设计思路

  • 统一处理图片路径,确保图片路径的正确性和完整性。
  • 提供工具函数,方便在各个页面调用。

代码实现逻辑

  • 检查图片路径是否为完整的URL,如果不是则拼接基础URL。
  • 如果是静态资源路径,直接返回。
  • 如果路径以 /adopt 开头,移除它并拼接基础URL。

调用过程

  • 各页面调用工具函数 -> 检查图片路径 -> 返回完整的图片URL。

代码片段 (utils/imagePathUtils.js 中的图片路径处理部分):

const getImageUrl = (path) => {
  if (!path) return '/static/images/tabbar/user.png';
  if (path.startsWith('http://') || path.startsWith('https://')) return path;
  if (path.startsWith('/static/')) return path;
  if (path.startsWith('/')) return `${app.globalData.baseUrl}${path}`;
  return `${app.globalData.baseUrl}/${path}`;
};

module.exports = { getImageUrl };

以上是对整个项目的前端业务功能的具体实现和调用过程的详细分析。

每个功能模块都遵循了清晰的设计思路、详细的代码实现逻辑和明确的调用过程,确保系统的稳定性和用户体验。

三、可能的答辩问题

一)如何处理用户登录状态?

解答:项目通过 wx.getStorageSync 方法从本地存储中获取用户的 tokenuserInfo,并在每次页面加载时进行验证。如果用户未登录或登录状态不完整,会提示用户先登录,并跳转到登录页面。此外,在 app.js 中,应用启动时也会检查登录状态,并通过 clearLoginState 方法清除不完整的登录状态。

代码片段

// 文件: adopt/apply.js
if (!token || !userInfo || !isLogin) {
  wx.showModal({
    title: '提示',
    content: '请先登录后再申请领养',
    success(res) {
      if (res.confirm) {
        wx.navigateTo({
          url: '/pages/user/login'
        });
      } else {
        wx.navigateBack();
      }
    }
  });
  return;
}