uni-app项目 懂你找图

322 阅读3分钟

懂你找图项目(uni-app 开发)项目记录

gitee地址

https://gitee.com/xuzeting/dnpicture

教学视频在b站上就能找到,搜索关键字懂你找图

快速使用说明

进入项目

npm install
npm run dev:mp-weixin

在微信开发者工具中导入 dist/dev/mp-weixin 即可体验

用脚手架创建 uni-app 项目
vue create -p dcloudio/uni-preset-vue my-project
安装插件
npm i sass-loader node-sass
编译成微信小程序
npm run dev:mp-weixin

创建 pages 页面下的 tabbar 上的五个 vue 组件,并在 page.json 中配置 pages 和 tabBar。

==对文件进行修改后,小程序容易报错,可以尝试重启==

==tip:在 vue 文件中导入 wxss 文件时,一定不要忘记加后缀==

首页推荐页面

uni-ui
npm install @dcloudio/uni-ui -D

script 中引用组件:

import { uniBadge } from '@dcloudio/uni-ui'
//import uniBadge from '@dcloudio/uni-ui/lib/uni-badge/uni-badge.vue' //也可使用此方式引入组件
export default {
  components: { uniBadge },
}

template 中使用组件:

<uni-badge text="1"></uni-badge>
<uni-badge text="2" type="success" @click="bindClick"></uni-badge>
<uni-badge text="3" type="primary" :inverted="true"></uni-badge>

使用分段器把首页的四个选项 bar 写好并完成样式。

异步请求封装

然后就是封装自己的异步请求,在 src 下简历 utils 文件,在下创建 request.js,在其中写入

// es6  promise 微信小程序的api的铺垫
export default (params) => {
  // 加载中
  uni.showLoading({
    title: '加载中',
  })

  return new Promise((resolve, reject) => {
    wx.request({
      ...params,
      success(res) {
        resolve(res.data)
      },
      fail(err) {
        reject(err)
      },
      complete() {
        uni.hideLoading()
      },
    })
  })
}

然后在 main.js 中引入上述文件并挂在到 vue 原型上

import request from './utils/request'
推荐页面后端接口

"http://157.122.54.189:9088/image/v3/homepage/vertical"

moment.js

利用 moment.js 第三方库来对时间戳进行修改

安装

npm i moment
//将时间戳进行修改
this.months.MM = moment(this.months.stime).format('MM')
this.months.DD = moment(this.months.stime).format('DD')
图片大小调整

在后端接口获取图片后,需要再加上大小参数,具体如下

<image
  mode="aspectFill"
  :src="item.thumb + item.rule.replace('$<Height>', 360)"
></image>
v-if 优化渲染
v-if="recommends.length>0"
滚动改造 scroll-view
<scroll-view
  class="recommend_view"
  scroll-y
  v-if="recommends.length > 0"
></scroll-view>
分页处理(触底加载新的图片)
    handleToLower() {
      /*
      1 修改参数  skip+=limit;
      2 重新发送请求 getList()
      3 请求回来成功  hots 数据的叠加
       */

      if (this.hasMore) {
        this.params.skip += this.params.limit;
        this.getList();
      } else {
        // 弹窗提示用户
        uni.showToast({
          title: "没有数据了",
          icon: "none",
        });
      }
    },


    //热门数据列表,利用es6拼接刷新
    this.hots = [...this.hots, ...result.res.vertical];

//判断是否还有新的数据
if (result.res.vertical.length === 0) {
          this.hasMore = false;
          uni.showToast({
            title: "没有更多数据了",
            icon: "none"
          });
          return;
        }

首页专辑页面

专辑页面后端接口

"http://157.122.54.189:9088/image/v1/wallpaper/album",

轮播
.album_swiper {
  swiper {
    // 750rpx 326.0869565217392
    // height: calc(750rpx / 2.3 );
    height: 326.1rpx;
    image {
      height: 100%;
    }
  }
}
<view class="album_swiper">
  <swiper autoplay indicator-dots circular>
    <swiper-item v-for="item in banner" :key="item.id">
      <image :src="item.thumb"></image>
    </swiper-item>
  </swiper>
</view>
<!-- 
      swiper 一定要去到b站上 搜索 “微信小程序 黑马程序员”  “swiper标签的介绍”
      1 自动轮播 autoplay
      2 指示器 indicator-dots 
      3 衔接轮播 circular

      4 swiper 
        默认的高度 150px
      5 image 
        默认的宽度 320px => 基本样式中 重置了 100%
        默认的高度 240px 
      6 计算图片的宽度和高度的比例
      7 把图片的比例也写到swiper标签样式 
      8 swiper-item 
        100% 
     -->
列表

文字单行显示且尾部省略号表示

text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;

父容器需要加上以下 css,防止其溢出撑开父容器

overflow: hidden;
分页处理
handleToLower() {
      // console.log("武汉加油!!");
      if (this.hasMore) {
        this.params.skip += this.params.limit;
        this.getList();
      } else {
        uni.showToast({
          title: "没有数据了",
          icon: "none"
        });
      }
    }
  }

请求函数

getList() {
      this.request({
        url: "http://157.122.54.189:9088/image/v1/wallpaper/album",
        data: this.params
      }).then(result => {
        if (this.banner.length === 0) {
          this.banner = result.res.banner;
        }

        if (result.res.album.length === 0) {
          this.hasMore = false;
          uni.showToast({
            title: "没有更多数据了",
            icon: "none"
          });
          return;
        }

        this.album = [...this.album, ...result.res.album];
      });
    },
专辑详情

在 pages 下增加 album 文件夹,下面加 index.vue 文件,并在 pages.json 中进行配置

给详情页传 id
:url="`/pages/album/index?id=${item.id}
onLoad(options) {
   console.log(options);//打印参数

   this.id = options.id;//将参数中的id赋值给全局变量id

  },
关于换行的一些 tip

如果接口返回的字符串中含有一些特殊符号如换行符‘\n’,则千万别用 view,要是用 text 标签

处理一个数组是否有内容
if (Object.keys(this.album).length === 0)
接口挂了

再尝试首页图片导航的时候,发现提供的接口已经挂了,所以采用王俊凯的那个接口进行统一替代

图片详情

全局存储数据和页面跳转

首先在各个需要传递参数的页面内传递参数

<go-detail :list="hots" :index="index">

然后在 components 下的 goDetail.vue 组件内接收参数

handleClick() {
      // 1 将数据缓存下拉
      getApp().globalData.imgList = this.list;
      getApp().globalData.imgIndex = this.index;
      uni.navigateTo({
        url: "/pages/imgDetail/index",//页面跳转
      });
    },
图片详情页获取全局存储的数据
  data() {
    return {
      imgDetail: {},
    };
  },
  onLoad() {
    console.log(getApp().globalData);//打印获取的列表
    const { imgList, imgIndex } = getApp().globalData;
    this.imgDetail = imgList[imgIndex];
  },
防止因数据还未加载而先渲染报错

在加载时先设置变量进行赋值,再传给标签进行渲染可防止报错

  onLoad() {
    console.log(getApp().globalData);
    const { imgList, imgIndex } = getApp().globalData;
    this.imgDetail = imgList[imgIndex];
    this.imgDetail.newThumb =
      this.imgDetail.thumb + this.imgDetail.rule.replace("$<Height>", 360);
    console.log(this.imgDetail.newThumb);
  },
加载缩略图
this.imgDetail.newThumb =
  this.imgDetail.thumb + this.imgDetail.rule.replace('$<Height>', 360)
uni-ui 的点赞和收藏图标
<text class="iconfont icondianzan">{{ imgDetail.rank }}</text>
<text class="iconfont iconshoucang">收藏</text>
再次使用 moment 库处理时间
this.imgDetail.cnTime = moment(this.imgDetail.atime * 1000).fromNow()

将格式处理成中文

moment.locale('zh-cn')
评论部分

获取评论接口数据

    getComments(id) {
      this.request({
        url: `http://157.122.54.189:9088/image/v2/wallpaper/wallpaper/${id}/comment`,
      }).then((result) => {
        console.log(result);
      });
    },

然后在 onload 中调用

this.getComments(this.imgDetail.id)
接口再次出了一些问题

由于原来接口请求返回内容中缺少了 album,所以我采用全局存储的 imgList 来取代 album,具体

如下

getComments(id) {
      this.request({
        url: `http://157.122.54.189:9088/image/v2/wallpaper/wallpaper/${id}/comment`,
      }).then((result) => {
        this.album = getApp().globalData.imgList;
        console.log(this.album, "hello");
        this.hot = result.res.hot;
        this.comment = result.res.comment;
      });
    },
只想循环第一项

目前只想到了在循环项上加上 v-if 限制,不过这样 eslint 会报错, 之后再完善吧

swiperAction 组件
/* 1 slot 2 对外提供数据 滑动的方向 */
<template>
  <view @touchstart="handleTouchstart" @touchend="handleTouchend">
    <slot></slot>
  </view>
</template>

<script>
export default {
  data() {
    return {
      // 按下的时间
      startTime: 0,
      // 按下的坐标
      startX: 0,
      startY: 0,
    }
  },
  methods: {
    // 用户按下屏幕
    handleTouchstart(event) {
      // console.log("手指按下屏幕");
      // console.log("按下:"+event.changedTouches[0].clientX);
      // console.log("按下:"+event.changedTouches[0].clientY);

      this.startTime = Date.now()
      this.startX = event.changedTouches[0].clientX
      this.startY = event.changedTouches[0].clientY
    },
    handleTouchend(event) {
      // console.log("手指离开屏幕");
      // console.log("离开:"+event.changedTouches[0].clientX);
      // console.log("离开:"+event.changedTouches[0].clientY);

      const endTime = Date.now()
      const endX = event.changedTouches[0].clientX
      const endY = event.changedTouches[0].clientY

      // 判断按下的时长
      if (endTime - this.startTime > 2000) {
        return
      }

      // 滑动的方向
      let direction = ''

      // 先判断用户滑动的距离 是否合法 合法:判断滑动的方向  注意 距离要加上绝对值
      if (
        Math.abs(endX - this.startX) > 10 &&
        Math.abs(endY - this.startY) < 10
      ) {
        // 滑动方向!!!
        direction = endX - this.startX > 0 ? 'right' : 'left'
      } else {
        return
      }

      // 用户做了合法的滑动操作
      // console.log(direction);
      this.$emit('swiperAction', { direction })
    },
  },
}
</script>

<style></style>

imgDetail 中的处理

handleSwiperAction(e) {
      /*
     1 用户 左滑 imgIndex++
     2 用户 右滑 imgIndex--
     3 判断 数组是否越界问题!!
     4 左滑 e.direction==="left"&&this.imgIndex<imgList.length-1
     5 右滑 e.direction==="right"&&this.imgIndex>0
      */
      const { imgList } = getApp().globalData;
      if (e.direction === "left" && this.imgIndex < imgList.length - 1) {
        //  可以进行 左滑 加载下一页
        this.imgIndex++;
        this.getData();
      } else if (e.direction === "right" && this.imgIndex > 0) {
        // 右滑 加载上一页
        this.imgIndex--;
        this.getData();
      } else {
        uni.showToast({
          title: "没有数据了",
          icon: "none",
        });
      }
    },
下载图片
    async handleDownload() {
      // uni.downloadFile
      // uni.saveImageToPhotosAlbum

      await uni.showLoading({
        title: "下载中",
      });

      // 1 将远程文件下载到小程序的内存中 tempFilePath
      const result1 = await uni.downloadFile({ url: this.imgDetail.img });
      const { tempFilePath } = result1[1];

      //  2 将小程序内存中的临时文件下载到本地上
      const result2 = await uni.saveImageToPhotosAlbum({
        filePath: tempFilePath,
      });
      // console.log(result2);
      // 3 提示用户下载成功
      // console.log("下载城市");

      uni.hideLoading();
      await uni.showToast({
        title: "下载成功",
        // icon
      });
    },

分类页面

温故滚动窗口,以及 flex 与滚动标签之间的小冲突解决
<scroll-view @scrolltolower="handleScrolltolower" enable-flex
//!!!!!!!!!!!必须加上这个才能使得felx布局生效 scroll-y
class="category_tab_content" >
.category_tab_content {
  display: flex;
  flex-wrap: wrap;
  height: calc(100vh - 36px); //这是关键!!!!!!!!!!!!!!!!!
  .cate_item {
    width: 33.33%;
    border: 5rpx solid #fff;
  }
}
解决一些分页小 bug
this.params.skip = 0 //将跳过数据重置
this.vertical = [] //将数据列表重置
点击两次相同分页呢?
if (this.current !== e.currentIndex) {
  this.current = e.currentIndex
} else {
  // 点击的是相同的标题
  return
}
使用统一组件显示页面时如何重新请求数据
 watch: {
    urlobj() {
      // console.log("参数发生变化了");
      //  console.log(this.urlobj);

      this.videowp=[];
      this.getList();
    }
  },