校园猫咪小程序

前言

前段时间上线了自己学校的猫猫小程序:是小猫呀。今天来给大家分享下这个项目,希望大家在学习后也能给自己的学校开发一个猫猫小程序,或者将这个小程序用作毕设或者课设之类的,也可以作为前端找工作的项目。

下面是小程序的功能介绍,感兴趣的小伙伴可以去搜索下我的小程序,或者直接扫码

功能介绍

首页

添加猫咪

图片榜单

猫脸识别

核心代码

项目涉及的模块众多,我们举几个代表性的例子

添加猫咪


   * 页面的初始数据
   */
  data: {
    tipText: '正在鉴权...',
    pickers: {
      gender: ['公', '母'],
      sterilized: [false, true],
      adopt: cat_status_adopt.map((x) => { return {desc: x} }),
      to_star: [false, true],
    },
    picker_selected: {},
    bottomShow: false,
    text_cfg: text_cfg
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: async function (options) {
    await this.loadPickers();
    cat_id = options.cat_id;
    if (await checkAuth(this, 2)) {
      await this.loadCat();
    }
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: async function () {
    await this.loadMorePhotos();
  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {

  },
  // 没有权限,返回上一页
  goBack() {
    wx.navigateBack();
  },
  // 检查权限

添加图片


Page({
  /**
   * 页面的初始数据
   */
  data: {
    isAuth: false,
    user: {},
    uploading: false,
    birth_date: '2008-01-01',
    photos: [],
    set_all: {},
    canUpload: false,
    text_cfg: config.text,
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: async function (options) {
    const db = await cloud.databaseAsync();
    const cat_id = options.cat_id;
    var catRes = await db.collection('cat').doc(cat_id).field({
      birthday: true,
      name: true,
      campus: true,
      _id: true
    }).get();
    this.setData({
      cat: catRes.data,
      birth_date: catRes.data.birthday || ''
    });
    //this.checkUInfo();

    // 获取一下现在的日期,用在拍摄日前选择上
    const today = new Date();
    var now_date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate();
    this.setData({
      now_date: now_date
    });

    this.setData({
      canUpload: await checkCanUpload()
    });

    // 获取一下手机平台
    const device = await wx.getSystemInfoSync();
    this.setData({
      isIOS: device.platform == 'ios'
    });
  },

  async onShow() {
    await getPageUserInfo(this);
  },

  onUnload: async function (options) {
    await this.ifSendNotifyVeriftMsg()
  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {
    const pagesStack = getCurrentPages();
    const path = getCurrentPath(pagesStack);
    const share_text = `来给${this.data.cat.name}添加照片 - ${config.text.app_name}`;
    return shareTo(share_text, path);
  },

  async chooseImg(e) {
    var res = await wx.chooseMedia({
      count: 20,
      mediaType: ['image'],
      sizeType: ["compressed"],
      sourceType: ['album', 'camera'],
    })
    var photos = [];
    for (var file of res.tempFiles) {
      // 需要压缩
      if (file.size > 512 * 1024) {
        file.path = await compressImage(file.tempFilePath, 30);
        console.log("compressed path:", file.path);
      } else {
        file.path = file.tempFilePath;
      }

      photos.push({
        file: file
      });
    }
    this.setData({
      photos: photos,
      set_all: {},
    });
  },

  // 点击单个上传
  async uploadSingleClick(e) {
    if (this.data.uploading) {
      console.log("uploading lock.");
      return;
    }
    this.setData({
      uploading: true
    })
    await requestNotice('verify');
    wx.showLoading({
      title: config.text.add_photo.success_tip_title,
      mask: true,
    });
    const currentIndex = e.currentTarget.dataset.index;
    const photo = this.data.photos[currentIndex];
    await this.uploadImg(photo);
    this.data.photos = this.data.photos.filter((ph) => {
      // 把已上传的图片从图片列表中去掉
      return ph.file.path != photo.file.path;
    });
    this.setData({
      uploading: false,
      photos: this.data.photos,
    });
    wx.hideLoading();
    wx.showModal({
      title: config.text.add_photo.success_tip_title,
      content: config.text.add_photo.success_tip_content,
      showCancel: false
    });
  },

  // 点击多个上传
  async uploadAllClick(e) {
    if (this.data.uploading) {
      console.log("uploading lock.");
      return;
    }
    const photos = []; // 这里只会保存可以上传的照片
    for (const item of this.data.photos) {
      if (item.shooting_date && item.file.path) {
        photos.push(item);
      }
    }
    if (photos.length == 0) {
      wx.showModal({
        title: config.text.add_photo.unfinished_tip_title,
        content: config.text.add_photo.unfinished_tip_content,
        showCancel: false
      });
      return;
    }
    await requestNotice('verify');
    for (let i = 0; i < photos.length; ++i) {
      wx.showLoading({
        title: '正在上传(' + (photos.length - i) + ')',
        mask: true,
      });
      await this.uploadImg(photos[i]);
      this.data.photos = this.data.photos.filter((ph) => {
        // 把已上传的图片从图片列表中去掉
        return ph.file.path != photos[i].file.path;
      });
    }
    this.setData({
      uploading: false,
      photos: this.data.photos,
    })
    wx.hideLoading();
    wx.showModal({
      title: config.text.add_photo.success_tip_title,
      content: config.text.add_photo.success_tip_content,
      showCancel: false
    });
  },

  async ifSendNotifyVeriftMsg() {
    const db = await cloud.databaseAsync();
    const subMsgSetting = await db.collection('setting').doc('subscribeMsg').get();
    const triggerNum = subMsgSetting.data.verifyPhoto.triggerNum; //几条未审核才触发
    // console.log("triggerN",triggerNum);
    var numUnchkPhotos = (await db.collection('photo').where({
      verified: false
    }).count()).total;

    if (numUnchkPhotos >= triggerNum) {
      await sendNotifyVertifyNotice(numUnchkPhotos);
      console.log("toSendNVMsg");
    }
  },

  async uploadImg(photo) {
    // multiple 表示当前是否在批量上传,如果是就不显示上传成功的弹框
    this.setData({
      uploading: true,
    });
    const cat = this.data.cat;
    const tempFilePath = photo.file.path;
    //获取后缀
    const index = tempFilePath.lastIndexOf(".");
    const ext = tempFilePath.substr(index + 1);

    let upRes = await cloud.uploadFile({
      cloudPath: cat.campus + '/' + generateUUID() + '.' + ext, // 上传至云端的路径
      filePath: tempFilePath, // 小程序临时文件路径
    });
    // 返回文件 ID
    console.log(upRes.fileID);

    // 添加记录
    const params = {
      cat_id: cat._id,
      photo_id: upRes.fileID,
      user_id: this.data.user._id,
      verified: false,
      shooting_date: photo.shooting_date,
      photographer: photo.pher
    };

    let dbAddRes = (await api.curdOp({
      operation: "add",
      collection: "photo",
      data: params
    })).result;
    console.log("curdOp(add-photo) result:", dbAddRes);
  },

  pickDate(e) {
    console.log(e);
    const index = e.currentTarget.dataset.index;
    this.setData({
      ["photos[" + index + "].shooting_date"]: e.detail.value
    });
  },
  inputPher(e) {
    const index = e.currentTarget.dataset.index;
    this.setData({
      ["photos[" + index + "].pher"]: e.detail.value
    })
  },

  // 下面是统一设置
  setAllDate(e) {
    const value = e.detail.value;
    var photos = this.data.photos;
    console.log(photos);
    for (var ph of photos) {
      ph.shooting_date = value;
    }
    this.setData({
      "set_all.shooting_date": value,
      photos: photos,
    });
  },
  setAllPher(e) {
    const photographer = e.detail.value;
    var photos = this.data.photos;
    for (var ph of photos) {
      ph.pher = photographer;
    }
    this.setData({
      "set_all.pher": photographer,
      photos: photos,
    });
  },

  // 移除其中一个
  removeOne(e) {
    const index = e.currentTarget.dataset.index;
    const photos = this.data.photos;
    const new_photos = photos.filter((ph, ind, arr) => {
      // 这个photo是用户点击的photo,在上面定义的
      return index != ind;
    });
    this.setData({
      photos: new_photos
    });
  },

  goBackIndex(e) {
    wx.switchTab({
      url: '/pages/genealogy/genealogy',
    });
  },

  getUInfo(e) {
    toSetUserInfo();
  }
})

猫猫主界面 


const default_png = undefined;

var catsStep = 1;
var loadingLock = 0; // 用于下滑刷新加锁

var pageLoadingLock = true; // 用于点击按钮刷新加锁

const tipInterval = 24; // 提示间隔时间 hours

// 分享的标语
const share_text = config.text.app_name + ' - ' + config.text.genealogy.share_tip;

Page({

  /**
   * 页面的初始数据
   */
  data: {
    cats: [],

    filters: [],
    filters_sub: 0, // 过滤器子菜单
    filters_legal: true, // 这组过滤器是否合法
    filters_show: false, // 是否显示过滤器
    filters_input: '', // 输入的内容,目前只用于挑选名字
    filters_show_shadow: false, // 滚动之后才显示阴影
    filters_empty: true, // 过滤器是否为空

    // 高度,单位为px(后面会覆盖掉)
    heights: {
      filters: 40,
    },
    // 总共有多少只猫
    catsMax: 0,

    // 加载相关
    loading: false, // 正在加载
    loadnomore: false, // 没有再多了

    // 领养状态
    adopt_desc: config.cat_status_adopt,
    // 寻找领养的按钮
    adopt_count: 0,

    // 广告是否展示
    ad_show: {},
    // 广告id
    ad: {},

    // 需要弹出的公告
    newsList: [],
    newsImage: "",

    text_cfg: config.text
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: async function (options) {
    // 从缓存里读取options
    var fcampus = options.fcampus;
    if (!fcampus) {
      fcampus = this.getFCampusCache();
    }
    // 从分享点进来,到跳转到其他页面
    if (options.toPath) {
      wx.navigateTo({
        url: decodeURIComponent(options.toPath),
      });
    }
    // 从扫描二维码扫进来,目前只用于猫猫二维码跳转
    if (options.scene) {
      const scene = decodeURIComponent(options.scene);
      console.log("scene:", scene);
      if (scene.startsWith('toC=')) {
        const cat_No = scene.substr(4);
        const db = await cloud.databaseAsync();
        var cat_res = await db.collection('cat').where({
          _no: cat_No
        }).field({
          _no: true
        }).get()

        if (!cat_res.data.length) {
          return;
        }
        const _id = cat_res.data[0]._id;
        this.clickCatCard(_id, true);
      }
    }
    // 开始加载页面
    const settings = await getGlobalSettings('genealogy');
    if (!settings) {
      console.log("no setting");
      return
    }
    // 先把设置拿到
    catsStep = settings['catsStep'];
    // 启动加载
    this.loadFilters(fcampus);

    this.setData({
      main_lower_threshold: settings['main_lower_threshold'],
      adStep: settings['adStep'],
      photoPopWeight: settings['photoPopWeight'] || 10
    });

    // 载入公告信息
    this.newsModal = this.selectComponent("#newsModal");
    await this.loadNews();

    // 设置广告ID
    const ads = await getGlobalSettings('ads') || {};
    this.setData({
      ad: {
        banner: ads.genealogy_banner
      },
    })
  },

  onShow: function () {
    showTab(this);
  },

  loadFilters: async function (fcampus) {
    // 下面开始加载filters
    var res = await loadFilter();
    if (!res) {
      wx.showModal({
        title: '出错了...',
        content: '请到关于页,清理缓存后重启试试~',
        showCancel: false,
      });
      return false;
    }
    var filters = [];
    var area_item = {
      key: 'area',
      cateKey: 'campus',
      name: '校区',
      category: []
    };
    area_item.category.push({
      name: '全部校区',
      items: [], // '全部校区'特殊处理
      all_active: true
    });
    // 用个object当作字典,把area分下类
    var classifier = {};
    for (let i = 0, len = res.campuses.length; i < len; ++i) {
      classifier[res.campuses[i]] = {
        name: res.campuses[i],
        items: [], // 记录属于这个校区的area
        all_active: false
      };
    }
    for (let k = 0, len = res.area.length; k < len; ++k) {
      classifier[res.area[k].campus].items.push(res.area[k]);
    }
    for (let i = 0, len = res.campuses.length; i < len; ++i) {
      area_item.category.push(classifier[res.campuses[i]]);
    }
    // 把初始fcampus写入,例如"011000"
    if (fcampus && fcampus.length === area_item.category.length) {
      console.log("fcampus exist", fcampus, area_item);
      for (let i = 0; i < fcampus.length; i++) {
        const active = fcampus[i] == "1";
        area_item.category[i].all_active = active;
      }
    }
    filters.push(area_item);

    var colour_item = {
      key: 'colour',
      name: '花色',
      category: [{
        name: '全部花色',
        items: res.colour.map(name => {
          return {
            name: name
          };
        }),
        all_active: true
      }]
    }
    filters.push(colour_item);

    var adopt_status = [{
      name: "未知",
      value: null
    }];
    adopt_status = adopt_status.concat(config.cat_status_adopt.map((name, i) => {
      return {
        name: name,
        value: i, // 数据库里存的
      };
    }));

    var adopt_item = {
      key: 'adopt',
      name: '领养',
      category: [{
        name: '全部状态',
        items: adopt_status,
        all_active: true
      }]
    }
    filters.push(adopt_item);

    // 默认把第一个先激活了
    filters[0].active = true;
    console.log(filters);
    this.newUserTip();
    this.setData({
      filters: filters,
    });
    await this.reloadCats();
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {
    this.getHeights();
  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: async function () {
    await this.loadMoreCats();
  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {
    // 分享是保留校区外显filter
    const pagesStack = getCurrentPages();
    const path = getCurrentPath(pagesStack);
    const fcampus = this.getFCampusStr();
    const query = `${path}fcampus=${fcampus}`;
    console.log(query);
    return {
      title: share_text,
      path: query
    };
  },

  // 获取二进制的campus filter字符串
  getFCampusStr: function () {
    var fcampus = [];
    for (var item of this.data.filters[0].category) {
      fcampus.push(item.all_active ? "1" : "0");
    }
    return fcampus.join("");
  },

  onShareTimeline: function () {
    return {
      title: share_text,
      // query: 'cat_id=' + this.data.cat._id
    }
  },

代码

GitHub - sysucats/zhongdamaopu: 中大猫谱小程序(小程序更名为笃行猫谱)

项目由中山大学的学长学姐搭建,部署文档也非常全面,大家可以参考学长学姐的文档去部署属于自己的猫猫小程序呀。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晓宜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值