懂你找图项目(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();
}
},