uniapp项目实战笔记

目录

  • 1 项目搭建
    • 1.1 新建项目并运行
    • 1.2 按照项目效果配置基本外观
  • 2 搭建框架
    • 2.1 配置tabbar
  • 3 从后台获取数据
    • 3.1 封装请求组件
    • 3.2 搭建后台应用
  • 4 首页页面
    • 4.1 配置首页基础数据
    • 4.2 轮播图
    • 4.3 页面中的导航
    • 4.4 首页推荐商品
  • 5 单个商品的详情页
    • 5.1 点击跳转
    • 5.2 获取详情页数据
    • 5.3 UI组件的使用
  • 6 超市首页
    • 6.1 完成基础布局
    • 6.2 触底加载
    • 6.3 下拉刷新
    • 6.4 点击跳转到详情页
  • 7. 联系我们
    • 7.1 静态页面结构
    • 7.2 动态
  • 8. 社区图片
  • 9. 咨讯
    • 9.1 封装基础组件
    • 9.2 跳转到详情页
  • 10 打包
    • 10.1 小程序打包
    • 10.2 H5打包
    • 10.3 App打包
  • 11 优化

项目源码地址:https://github.com/whynot-todo/uniapp_demo.git

1 项目搭建


1.1 新建项目并运行


进行项目的搭建,详情点击

  • 这里我们创建的名称为uniapp_demo
  • 运行,这里我们看上面的详情点击就可以,初次运行注意路径的配置
  • 当我们运行起来收的界面式这样的
    uniapp项目实战笔记_第1张图片

1.2 按照项目效果配置基本外观


  1. 打开pages.json,在globalStyle下配置全局的样式,你可以参照官网来进行配置,详情点击
"globalStyle": {
     
        "navigationBarTextStyle": "white",
        "navigationBarTitleText": "商城",
        "navigationBarBackgroundColor": "##b50e03",
        "backgroundColor": "#F8F8F8"
}

配置好后是这个样子的,因为局部配置的优先级较高,所以navigationBar显示的是uniapp
uniapp项目实战笔记_第2张图片

2 搭建框架


2.1 配置tabbar

1. 新建页面

根据效果图,tabber联系着四个页面,分别是首页、咨询、购物车、会员,我们新建四个页面分别是

  1. pages/index/index
  2. pages/cart/cart
  3. pages/member/member
  4. pages/news/news

2. 新建页面步骤

  1. pages文件夹下右击,选择新建页面
    uniapp项目实战笔记_第3张图片
  2. 然后对页面进行少许的配置
    uniapp项目实战笔记_第4张图片
    需要注意的是
  • 选择模板,这里是使用了scss
  • 是否在pages.json中注册路径,这里的四个我们都需要
  1. 按照上面的步骤新建完四个文件

3. 配置tabber
关于tabbar的配置,详情点击

	"tabBar": {
     
		"selectedColor": "#b50e03",
		"color": "#ccc",
		"list": [{
     
				"text": "首页",
				"pagePath": "pages/index/index",
				"iconPath": "static/icon/home.png",
				"selectedIconPath": "static/icon/home-active.png"
			},
			{
     
				"text": "资讯",
				"pagePath": "pages/news/news",
				"iconPath": "static/icon/news.png",
				"selectedIconPath": "static/icon/news-active.png"
			},
			{
     
				"text": "购物车",
				"pagePath": "pages/cart/cart",
				"iconPath": "static/icon/cart.png",
				"selectedIconPath": "static/icon/cart-active.png"
			},
			{
     
				"text": "会员",
				"pagePath": "pages/member/member",
				"iconPath": "static/icon/member.png",
				"selectedIconPath": "static/icon/member-active.png"
			}
		]
	}

需要注意的是

  • 配置的顺序,pages数组的第一个元素表示启动时的默认页
  • 路径的书写

当我们把上面配置好后,就会出现以下效果,表示已经配置成功了
uniapp项目实战笔记_第5张图片

3 从后台获取数据

3.1 封装请求组件

  1. 我们在根目录下新建utils文件夹,并新建api.js文件,写上如下代码
const BASE_URL = 'http://localhost:8082'
export const myRequest = (options)=>{
     
	return new Promise((resolve,reject)=>{
     
		uni.request({
     
			url:BASE_URL+options.url,
			method: options.method || 'GET',
			data: options.data || {
     },
			success: (res)=>{
     
				if(res.data.status !== 0) {
     
					return uni.showToast({
     
						title: '获取数据失败'
					})
				}
				resolve(res)
			},
			fail: (err)=>{
     
				uni.showToast({
     
					title: '请求接口失败'
				})
				reject(err)
			}
		})
	})
}

需要注意以下几点

  • 我们要返回一个promise对象
  • 函数接收的参数是一个配置对象,他应该包括接口文档中的所需要的三个数据
{
     
url,//请求的路径,
method,//请求方法
data//请求需要的参数
}
  • 对于成功和失败的处理uni.showToast 详情点击
  • BASE_URL是后台的接口,uni.request详情点击,对于H5端需要做跨域的处理,详情点击,我们这里主要写小程序,所以就不处理了。
  1. 将请求方法添加到全局
    打开mian.js => 引入组件 => 将方法添加到原形中
import {
     myRequest} from 'utils/api.js'
Vue.prototype.$myRequest = myRequest

3.2 搭建后台应用

  1. 下载phpStudy,并打开,这里使用的版本为8.1.0.1
  2. 启动MYSQL服务
  3. 点击打开,点击SQL_Front
    uniapp项目实战笔记_第6张图片
  4. 点击打开
    uniapp项目实战笔记_第7张图片
  5. 右击localhost,选择新建,选择数据库
    uniapp项目实战笔记_第8张图片
  6. 输入数据库名称(要与我们的本地sql文件相同),点击确定
    uniapp项目实战笔记_第9张图片
  7. 在我们新建的数据库上右击,点击输入,点击sql文件,
    uniapp项目实战笔记_第10张图片
  8. 将本地文件进行导入,注意字符集为utf-8
    uniapp项目实战笔记_第11张图片
  9. 导入之后我们可以关闭,当前的页面,但不能退出phpStudy
  10. 打开服务器文件夹 ,运行命令node app.js,将后台的项目运行起来。
    在这里插入图片描述

4 首页页面


4.1 配置首页基础数据


navigationBarTitleText

{
     
	"path": "pages/index/index",
	"style": {
     
		"navigationBarTitleText": "商城首页"
	}
}

配置之后
在这里插入图片描述

4.2 轮播图


获取轮播图数据

  • 接口文档
    uniapp项目实战笔记_第12张图片
  • 根据接口文档,定义方法
data() {
     
	return {
     
		swipers: []
},
methods: {
     
	async getSwipers () {
     
		const res = await this.$myRequest({
     
			url: '/api/getlunbo'
		})
		this.swipers = res.data.message
	},
}
  • 在生命周期函数中进行调用
onLoad() {
     
	this.getSwipers()
	/* console.log(this.swipers) 得不到相应的数据 */
},
  • 有的小伙伴可能会在onLoad函数中直接打印this.swipers注意这样是无法得到相应的数据的,因为他是一个异步函数

将获取到的数据显示到网页中

  • 我们需要使用swiper组件,详情点击
<swiper indicator-dots circular autoplay indicator-color='rgba(249,180,141,.7)'>
	<swiper-item v-for="item in swipers" :key="item.id">
		<image :src="item.img">image>
	swiper-item>
swiper>
  • 我们还需要给swiper一个宽度和高度
swiper{
     
	width: 750rpx;
	height: 380rpx;
	image{
     
		height: 100%;
		width: 100%;
	}
}

说明

  • indicator-dots为指示小圆点
  • circular循环滑动
  • autoplay自动轮播
  • indicator-color 原点背景颜色

4.3 页面中的导航


1. 静态的导航


  1. 打开素材/font_nav将我们下载好的字体图标放到static/fonts文件夹下,我们只需要其中的五个文件
    • iconfont.woff
    • iconfont.woff2
    • iconfont.ttf
    • svg
    • eot

uniapp项目实战笔记_第13张图片
2. 打开素材/font_nav中的iconfont.css文件,将代码复制,放到App.vue文件下,注意要修改引用的路径以~@开头,写路径,@后面不能跟 .,我们使用的时候就可以class="iconfont icon-name"

    @font-face {
     font-family: "iconfont";
      src: url('~@/static/fonts/iconfont.eot?t=1576335469449'); /* IE9 */
      src: url('~@/static/fonts/iconfont.eot?t=1576335469449#iefix') format('embedded-opentype'), /* IE6-IE8 */
      url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAVMAAsAAAAAChwAAAT/AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDKAqHQIYhATYCJAMUCwwABCAFhG0HUBurCFGUTk6f7EeBu6eIlFFajBY208XLlGHx/6/hiYfvx76d+2xFJLlUn75pp4tCoyQ8NgiJUJjuJfzNpSrpXIDoeDbs3GjyH7Lfv+zTy1mok+GyEhYAW8AKaPvz/6eeG1QvDUo6SQW8bE4KYTDSMRj1R/5x7/TP88Dmo7Jc/misYXOelwWYYEBjbGxrAQ7KEvQW8SwuJBO4nkCvRQ21q/PbB5hXQLdAvDU6gHmbRQnhhG598xlb1lEvsu70PvEBfFU/H/+hF1IkNYO+149nMRz+6v7/fwrv4JAqI6T5uXCbRMYWUIhnX8+tKT22ZUrvJ3PaCdCNryR+Sb2RfRPT+c9kbdF0s788siIRDWj6GXkWL/ySEsVkfsmyftoYKiIZsiQfJ+Y2ll7MgdgDiE/AMF+CumLWsCSpdFXpjsNkefoatW8WSY8xapRhreWm1a9vWvVEM6edaQdPapImTzTLLG4BwEBG/HJsK4bVEKkStJbxbUYy3othvO2ofJA7jBARcUZ4illDuEL29TB2C/3AGWj4WUfGYbG25LTICaEk1jWBR8KWC0bqQJ+EjJYmOnvisiHzqtifvZ08X7NYzBaJWAJBglDIRIi3Fd/C3cbZjqEKvkh9Qmx2SKCgMAhD15VHVkCQMCWOP72YnLB0euHUpROzJ/PEYiR6wxK8ShC+ZsryVibGlbIzorOjqRKnhwyOKTIlMRoPSLgMlYvInDIKdwsQ8K2YbO2PReRsyxzZvrK0dWxUYUrIE2sPj8+sHNw6tUVhj0AgslvelUfhDnNG4k0YwgedbyX/ovPRL8SnqwOt/atMII/RGGMacyXGJOaq+MtGYDFMOL1Hk8OSJdfqARNMZkPmnDVh5DP0bjDpx6XAB6RO0JkqM4F0fNSNZMKXB20QqI//n+swdzCvnv1/JszOzMEszPsskD8RZtrarNB14gGhubjWlduokVnWD9e9PyCQd1jtvPjStKTGs6VZFT/xjhhW0hpkG6PoC0wW8iGUrPK9prFY4AM+9GF8APhKfIvr5tct+IoAxU/eE3ILa4yrDxTgUfkrG0lPTtshU4pGtXT9UJqywWkDhzyi/5ka15D+WlPn1Gd+QYF7s0j3dXTuS0oStZLum6on86NOwIbPcydIP3+STiDFaYk1Z6WFLPt7b7M6EhMA+Gr+qtDilfia4mPFr0p8JZUS973QHulzYJxsPjJ6xdeW7Wd75WHHrZsGDXaUb1hacCuQVmrOkt1DjdgTxSeY3EcWDqCzRLxB9PmH2DNplYhzhCDbNLfzY7dKy1jb/dqNFfBTXbWg8vhWsH1JP9IN0VQmIcif4isHay1vtVDzNIiyxeirbpZ0JKEXg3QAAqN+8jFMLN9E6DZhQtJlAbJuS2ght6DqcwRNt1Potelicp8xbEqUHmz4IBCGfUIy6DtkwyZoIb+hmvQLzXAQoddNbM3ZZyWKFk+YAqEYwxGayJZKsMuiVnxH2ucBW+Uq94m4MkHstjrO5AWVxHkMqPq6J6JQsS3wnGxGeW7RsU0pklYi4rbbbeV3plZkC1h0iZGAIDEUGkFGxCopubpYdL3/DtG8XIBDSn4knxBWMbWjrpZODPRCU8YqWZfGlT6tRwjFOynMKqBz6iI5xizk/FulSES0JCkizrY2q6Tialrl64ppLD0VysKcNVLkKFGjae8S40w5K14OB+WS9lKjkR/YgsrZsRnZzAAA') format('woff2'),
      url('~@/static/fonts/iconfont.woff?t=1576335469449') format('woff'),
      url('~@/static/fonts/iconfont.ttf?t=1576335469449') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
      url('~@/static/fonts/iconfont.svg?t=1576335469449#iconfont') format('svg'); /* iOS 4.1- */
    }

    .iconfont {
     
      font-family: "iconfont" !important;
      font-size: 16px;
      font-style: normal;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    }

    .icon-shipin:before {
     
      content: "\f0024";
    }

    .icon-tupian:before {
     
      content: "\e650";
    }

    .icon-guanyuwomen:before {
     
      content: "\e608";
    }

    .icon-ziyuan:before {
     
      content: "\e60d";
    }
  1. 我们在index.vue中写上如下结构
<view class="nav">
    <view class="item">
        <view class="iconfont icon-ziyuan">view>
        <text>超市首页text>
    view>
    <view class="item">
        <view class="iconfont icon-tupian">view>
        <text>联系我们text>
    view>
    <view class="item">
        <view class="iconfont icon-guanyuwomen">view>
        <text>社区图片text>
    view>
    <view class="item">
        <view class="iconfont icon-shipin">view>
        <text>视频专区text>
    view>
view>
  1. 我们在uni.scss中定义一个主题颜色,如果使用的是less我们可以新建一个全局文件定义变量
/* 颜色变量 */
$shop-color: #b50e03;
  1. index.vue添加CSS样式,这里使用的是flex和百分比布局,并引用全局变量
.nav{
     
  display: flex;
  align-items: center;
  .item{
     
    width: 25%;
    text-align: center;
    view{
     
      background: $shop-color;
      line-height: 120rpx;
      width: 120rpx;
      height: 120rpx;
      border-radius: 90px;
      margin:10px auto;
    }
    text{
     
      font-size: 15px;
    }
  }
  .iconfont{
     
    font-size: 25px;
    color: #fff;
    height: 50px;
  }
  .icon-tupian{
     
    font-size: 20px;
  }
}

当我们配置完后,就会出现以下画面

uniapp项目实战笔记_第14张图片
动态的页面导航


我们为四个导航按钮新建四个页面,上面有配置页面的步骤在搭建框架

  1. /pages/goods/goods
  2. /pages/pics/pics
  3. /pages/contact/contact
  4. /pages/videos/videos

我们需要为四个图标定义点击跳转事件,我们需要用到uni.navigateTo(OBJECT) 详情点击

优化导航结构


  1. 因为一个一个的绑定优点麻烦,我们对于导航的结构优化一下,在index.vuedata(){}中,添加如下配置数据
navs: [
  {
     
    icons: "iconfont icon-ziyuan",
    title: "超市首页",
    path: "/pages/goods/goods"
  },
  {
     
    icons: "iconfont icon-tupian",
    title: "社区图片",
    path: "/pages/pics/pics"
  },
  {
     
    icons: "iconfont icon-guanyuwomen",
    title: "联系我们",
    path: "/pages/contact/contact"
  },
  {
     
    icons: "iconfont icon-shipin",
    title: "学习视频",
    path: "/pages/videos/videos"
  }
]
  1. 将上面的重复结构删掉,我们使用v-for遍历渲染结构,并定义点击跳转的方法
<view class="nav">
  <view class="item" v-for="(item,index) in navs" :key="index" @click="navItemClick(item.path)">
    <view :class="item.icons">view>
    <text>{
    {item.title}}text>
  view>
view>
  1. 我们在methods中定义点击的方法
navItemClick(url) {
     
	uni.navigateTo({
     
		url
	})
}
  1. 之后我们就可以点击跳转了

4.4 首页推荐商品


单个的静态结构


  1. 我们根据效果图先写一个单个的静态结构

<view class="hot_goods">
	<view class="tit">推荐商品view>
	
	<view class="goods_list">
		<view class="goods_item">
			<image>image>
			<view class="price">
				<text>1299text>
				<text>12990text>
			view>
			<view class="name">华为(HUAWEI)荣耀6Plus 16G双4G版view>
		view>
	view>
view>
  1. css样式
.hot_goods {
     
	background: #eee;

	.tit {
     
		border-top: 2px solid #eee;
		border-bottom: 2px solid #eee;
		margin-top: 20px;
		margin-bottom: 3px;
		color: $shop-color;
		height: 50px;
		line-height: 50px;
		text-align: center;
		letter-spacing: 20px;
		background: #fff;
	}

	.goods_list {
     
		display: flex;
		padding: 0 15rpx;
		justify-content: space-between;
		overflow: hidden;
		flex-wrap: wrap;

		.goods_item {
     
			width: 355rpx;
			margin-bottom: 15rpx;
			background: #fff;
			padding: 10px;
			box-sizing: border-box;

			image {
     
				height: 150px;
				width: auto;
				mix-width: 160px;
				margin: 10px auto;
			}

			.price {
     
				font-size: 18px;
				color: red;
				padding: 8px 0;

				text:nth-child(2) {
     
					color: #ccc;
					text-decoration: line-through;
					margin-left: 10px;
					font-size: 13px;
				}
			}

			.name {
     
				font-size: 14px;

			}
		}
	}
}

商品组件


  1. 观看效果图,我们发现商品卡片是重复的结构,我们可以把它抽成一个组件
  2. 我们在根目录下新建components/goods-list文件夹,并新建goods-list.vue文件

uniapp项目实战笔记_第15张图片
3. 我们把相应的代码复制到goods-list.vue

<template>
	<view>
		
		<view class="goods_list">
			<view class="goods_item">
				<image>image>
				<view class="price">
					<text>1299text>
					<text>12990text>
				view>
				<view class="name">华为(HUAWEI)荣耀6Plus 16G双4G版view>
			view>
		view>
	view>
template>

<script>
	export default {
      
		data() {
      
			return {
      

			};
		}
	}
script>

<style lang="less">
	.goods_list {
      
		display: flex;
		padding: 0 15rpx;
		justify-content: space-between;
		overflow: hidden;
		flex-wrap: wrap;

		.goods_item {
      
			width: 355rpx;
			margin-bottom: 15rpx;
			background: #fff;
			padding: 10px;
			box-sizing: border-box;

			image {
      
				width: 80%;
				height: 150px;
				display: block;
				margin: auto;
			}

			.price {
      
				font-size: 18px;
				color: red;
				padding: 8px 0;

				text:nth-child(2) {
      
					color: #ccc;
					text-decoration: line-through;
					margin-left: 10px;
					font-size: 13px;
				}
			}

			.name {
      
				font-size: 14px;
			}
		}
	}
style>
  1. index.vue中引入并映射成组件
import GoodsList from '../../components/goods-list/goods-list.vue'

components:{
     
	GoodsList
}
  1. 模板中
<view class="hot_goods">
	<view class="tit">推荐商品view>
	<goods-list>goods-list>
view>
  1. 效果图

uniapp项目实战笔记_第16张图片

获取动态的数据


  1. index.vue,methods中定义方法获取数据

接口文档
uniapp项目实战笔记_第17张图片

methods

// 获取热门商品列表数据
async getHotGoods () {
     
	const res = await this.$myRequest({
     
		url: '/api/getgoods?pageindex=1'
	})
	this.goods = res.data.message
},

data

data(){
     
	return {
     
		goods: []
	}
}
  1. 在声明周期函数中,发送请求
onLoad() {
     
	this.getHotGoods()
},

将数据传递到子组件标签

  1. 传递
<goods-list :goods="goods">goods-list>
  1. 子组件标签接收数据,并依据接口文档进行数据的渲染goods-list
export default {
     
	props:['goods'],
}

template

<view class="goods_list">
	<view class="goods_item" v-for="item in goods" :key="item.id">
		<image :src="item.img_url">image>
		<view class="price">
			<text>¥{
    {item.sell_price}}text>
			<text>¥{
    {item.market_price}}text>
		view>
		<view class="name">{
    {item.title}}view>
	view>
view>

配置完之后的效果
uniapp项目实战笔记_第18张图片

5 单个商品的详情页

5.1 点击跳转


效果
uniapp项目实战笔记_第19张图片
我们新建一个页面为goods-detail
uniapp项目实战笔记_第20张图片
绑定点击监听


为每一个商品绑定点击监听

index.vue

<goods-list @goodsItemClick="goGoodsDetail" :goods="goods">goods-list>

定义方法

goGoodsDetail (id) {
     
	uni.navigateTo({
     
		url: '/pages/goods-detail/goods-detail?id='+id
	})
}

子组件触发方法

goods-list.vue

<view class="goods_item" v-for="item in goods" :key="item.id" @click="navigator(item.id)">

定义方法

methods: {
     
	navigator (id) {
     
		this.$emit('goodsItemClick',id)
	}
}

5.2 获取详情页数据


在生命周期中得到,传来的数据


得到相应的数据,并在data中保存

data() {
     
	return {
     
		id: 0,
},
onLoad (options) {
     
	this.id = options.id
}

获取展示轮播图数据


我们观察到展示的商品图片是一个轮播的形式,我们可以先写静态组件,这样也是比较科学,这里就不展示了,直接获取数据写结构

接口文档
uniapp项目实战笔记_第21张图片
根据接口文档写请求方法

data() {
     
	return {
     
		id: 0,
		swipers: [],
	}
},
methods: {
     
	async getSwipers () {
     
		const res = await this.$myRequest({
     
			url: '/api/getthumimages/'+this.id
		})
		this.swipers = res.data.message
	},
}

在生命周期函数中执行

onLoad (options) {
     
	this.getSwipers()
},

页面结构


<view class="goods_detail">
	<swiper indicator-dots>
		<swiper-item v-for="(item,index) in swipers" :key="index">
			<image :src="item.src">image>
		swiper-item>
	swiper>
view>

css

swiper{
     
	height: 700rpx;
	image{
     
		width: 100%;
		height: 100%;
	}
}

当我们把上面写好之后,详情轮播图已经写好了。

uniapp项目实战笔记_第22张图片

获取商品信息


接口文档
uniapp项目实战笔记_第23张图片
根据接口文档写方法

data() {
     
	return {
     
		info: {
     },
},
async getDetailInfo () {
     
	const res = await this.$myRequest({
     
		url: '/api/goods/getinfo/'+this.id
	})
	this.info = res.data.message[0]
},

生命周期中调用方法

onLoad (options) {
     
	this.id = options.id
	this.getDetailInfo()
},

页面结构

<view class="box1">
	<view class="price">
		<text>¥{
    {info.sell_price}}text>
		<text>¥{
    {info.market_price}}text>
	view>
	<view class="goods_name">{
    {info.title}}view>
view>
<view class="line">view>
<view class="box2">
	<view>货号:{
    {info.goods_no}}view>
	<view>库存:{
    {info.stock_quantity}}view>
view>
<view class="line">view>

样式

.box1 {
     
	padding: 10px;
	.price {
     
		font-size: 35rpx;
		color: $shop-color;
		line-height: 80rpx;
		text:nth-child(2){
     
			color: #ccc;
			font-size: 28rpx;
			text-decoration: line-through;
			margin-left: 20rpx;
		}
	}
	.goods_name{
     
		font-size: 32rpx;
		line-height: 60rpx;
	}
}
.box2 {
     
	padding: 0 10px;
	font-size: 32rpx;
	line-height: 70rpx;
}
.line {
     
	height: 10rpx;
	width: 750rpx;
	background: #eee;
}

效果图
uniapp项目实战笔记_第24张图片
获取商品介绍数据


接口文档
uniapp项目实战笔记_第25张图片
根据接口文档,写请求方法

data() {
     
	return {
     
		content: '',
},
async getDetailContent () {
     
	const res = await this.$myRequest({
     
		url: '/api/goods/getdesc/'+this.id
	})
	this.content = res.data.message[0].content
},

在生命周期中执行方法

onLoad (options) {
     
	this.getDetailContent()
},

页面结构
注意到放回的数据是富文本,所以这里我们需要做特殊处理,使用,详情点击

<view class="box3">
	<view class="tit">详情介绍view>
	<view class="content">
		<rich-text :nodes="content">rich-text>
	view>
view>

css

.box3 {
     
	padding-bottom: 50px;
	.tit{
     
		font-size: 32rpx;
		padding-left: 10px;
		border-bottom: 1px solid #eee;
		line-height: 70rpx;
	}
	.content {
     
		padding: 10px;
		font-size: 28rpx;
		color: #333;
		line-height: 50rpx;
	}
}

当我们将上面的代码写完的时候,就会出现以下效果图
uniapp项目实战笔记_第26张图片
我们发现整个图片没有完全显示,我们观看以下打印的数据,发现每个图片上都有一个类属性gomeImgLoad,我们给他一个宽度,一定要注意放在全局样式中App.vue

.gomeImgLoad{
     
	width: 750rpx;
	height: auto;
}

再次刷新,我们发现已经好了

5.3 UI组件的使用

寻找组件

  1. 打开uniapp的官网,点击组件,找到扩展组件,找到商品导航

uniapp项目实战笔记_第27张图片
2. 点开网页之后,我们导入组件,点击使用HBuilderX导入组件

uniapp项目实战笔记_第28张图片
3. 然后选择导入的项目就可以了
4. 我们将官网上面的代码,复制到我们的项目

引入组件

import uniGoodsNav from '@/components/uni-goods-nav/uni-goods-nav.vue'
export default {
     
    components: {
     uniGoodsNav}
}

使用组件

<view class="goods_nav">
	<uni-goods-nav :fill="true" :options="options" :buttonGroup="buttonGroup" @click="onClick" @buttonClick="buttonClick" />
view>
export default {
     
    data () {
     
      return {
     
        options: [{
     
            icon: 'headphones',
            text: '客服'
        }, {
     
            icon: 'shop',
            text: '店铺',
            info: 2,
            infoBackgroundColor:'#007aff',
            infoColor:"red"
        }, {
     
            icon: 'cart',
            text: '购物车',
            info: 2
        }],
        buttonGroup: [{
     
          text: '加入购物车',
          backgroundColor: '#ff0000',
          color: '#fff'
        },
        {
     
          text: '立即购买',
          backgroundColor: '#ffa200',
          color: '#fff'
        }
        ]
      }
    },
    methods: {
     
      onClick (e) {
     
        uni.showToast({
     
          title: `点击${
       e.content.text}`,
          icon: 'none'
        })
      },
      buttonClick (e) {
     
        console.log(e)
        this.options[2].info++
      }
    }
}
  1. 我们可以选择将其固定在页面的底部,在goods-detail.vue中写上如下代码
.goods_nav {
     
	position: fixed;
	bottom:0;
	width: 100%;
}
  1. 我们可以根据配置选择我们想要的功能,详情点击

6 超市首页


6.1 完成基础布局


这里的商品展示其实是和我们的首页的展示是差不多的,我们就可以利用首页的组件来进行作品的展示。

  • 打开pages/goods/goods.vue文件,引入我们定义的goods-list.vue文件,并映射成组件。
import goodsList from '../../components/goods-list/goods-list.vue'
components: {
     "goods-list":goodsList},
<view class="goods_list">
	<goods-list >goods-list>
view>

6.2 触底加载


触底加载属于生命周期函数,详情点击
获取数据

获取数据。与首页不同的是,首页只获取了一页数据,而这里到达底部之后会再次获取数据

接口文档
uniapp项目实战笔记_第29张图片
根据接口文档定义方法

data() {
     
	return {
     
		pageindex: 1,
		goods: [],
	}
},
methods: {
     
	// 获取商品列表数据
	async getGoodsList() {
     
		const res = await this.$myRequest({
     
			url: '/api/getgoods?pageindex='+this.pageindex
		})
		this.goods = res.data.message
	},
}

在生命周期中执行

onLoad () {
     
	this.getGoodsList()
},

向组件中传递数据

<goods-list  :goods="goods">goods-list>

完成后,页面的效果如下
uniapp项目实战笔记_第30张图片
触底加载

  • 触底加载需要的生命周期函数为onReachBottom,触底加载主要用于加载第二个页面的数据,所以写如下代码
onReachBottom() {
     
	this.pageindex++
	this.getGoodsList()
},
  • 对于请求函数做修改
/* this.goods = res.data.message */
this.goods = [...this.goods,...res.data.message]

触底优化

当数据加载完毕的时候,提示数据加载完毕

<view class="goods_list">
	<goods-list :goods="goods">goods-list>
	<view class="isOver" v-if="flag">-----我是有底线的-----view>
view>
  • flag为标识是否触底
data() {
     
	return {
     
		flag: false
	}
},

onReachBottom生命周期函数中加上这样一段代码

  • 每次请求增加是个商品
if(this.goods.length<this.pageindex*10) return this.flag = true

修饰提示线

.goods_list {
     
	background: #eee;;
}
.isOver {
     
	width: 100%;
	height: 50px;
	line-height: 50px;
	text-align: center;
	font-size: 28rpx;
}

6.3 下拉刷新


详情点击

  1. 根据官方文档,我们要首先开启下拉刷新,打开pages.json,我们顺着可以改一下导航的文字
"style": {
     
	"navigationBarTitleText": "商品列表",
	"enablePullDownRefresh": true
}
  1. 下拉刷新函数
onPullDownRefresh() {
     
	console.log('下拉刷新了')
	this.pageindex = 1
	this.goods = []
	this.flag = false
	this.getGoodsList()
}
  • 需要注意的是我们要停止下拉刷新,但是我们要考虑什么时候停止下拉刷新,因为请求是一个异步函数,一般我们都应该是在请求数据之后停止下拉刷新,所以我们对于请求函数和下拉刷新函数优化一下
onPullDownRefresh() {
     
	console.log('下拉刷新了')
	this.pageindex = 1
	this.goods = []
	this.flag = false
	setTimeout(()=>{
     
	  this.getGoodsList(()=>{
     
			uni.stopPullDownRefresh()
		})	
	},1000)
},
async getGoodsList(callBack) {
     
	const res = await this.$myRequest({
     
		url: '/api/getgoods?pageindex='+this.pageindex
	})
	this.goods = [...this.goods,...res.data.message]
	callBack && callBack()
},

6.4 点击跳转到详情页


在我们写首页数据的时候,在goods-list.vue组件中已经定义了一个函数goodsItemClick,我们在此组件中再次写方法,当然你可以选择复制粘贴index.vue

<goods-list  @goodsItemClick="goGoodsDetail" :goods="goods">goods-list>
goGoodsDetail (id) {
     
	uni.navigateTo({
     
		url: '/pages/goods-detail/goods-detail?id='+id
	})
}

7. 联系我们


7.1 静态页面结构


打开contact.vue根据效果图搭建静态的页面结构

<view class="contact">
	<image class="img" src="http://www.itcast.cn/2018czydz/images/gywmban.jpg">image>
	<view class="info">
		<view @click="phone">联系电话:400-618-9090 ( 点击拨打 )view>
		<view>地址:xxxxxview>
	view>
	<map class="map" :longitude="longitude" :scale="scale" :latitude="latitude" :markers="markers">map>
view>

样式

.contact{
     
	.img{
     
		width: 750rpx;
		height: 320rpx;
	}
	.info{
     
		padding: 10rpx 20rpx;
		font-size: 30rpx;
		view{
     
			line-height: 80rpx;
			border-bottom: 1px solid #eee;
		}
	}
	
	.map{
     
		width: 750rpx;
		height: 750rpx;
	}
}
  • 在这里我们使用了一个组件:map 详情点击
  • 并定义了一个点击的方法,用来点击拨打电话
  • 注意我们需要给地图组件一个宽高

7.2 动态


export default {
     
	data() {
     
		return {
     
			longitude: 120.363172,
			latitude: 30.312212,
			scale: 13,
			markers:[
				{
     
					longitude: 120.363172,
					latitude: 30.312212,
					iconPath: '../../static/logo.png',
					width: 30,
					height: 30
				}
			]
		}
	},
	methods: {
     
		phone() {
     
			uni.makePhoneCall({
     
				phoneNumber: '400-618-9090'
			})
		}
	}
}

这里我们只用了一个api,makePhoneCall他是用来打电话的,详情点击

8. 社区图片


打开pics.vue,观察效果图,我们发现他的左右两个部分,都属于滚动的部分,这就需要我们的组件来控制scroll-view,详情点击

我们可以将静态的组件写好,再去写动态的组件,这里就不演示了,直接从后台获取数据

接口文档

  • 获取左侧图片的分类

uniapp项目实战笔记_第31张图片

  • 获取右侧的图片

uniapp项目实战笔记_第32张图片

写请求的方法

data() {
     
	return {
     
		cates: [],
		active: 0,
		secondData: []
	}
},
methods: {
     
	async getPicsCate () {
     
		const res = await this.$myRequest({
     
			url: '/api/getimgcategory'
		})
		this.cates = res.data.message
		this.leftClickHandle(0,this.cates[0].id)
	},
	async leftClickHandle (index,id) {
     
		this.active = index
		// 获取右侧的数据
		const res = await this.$myRequest({
     
			url: '/api/getimages/'+id
		})
		this.secondData = res.data.message
	},
}
  • active标识当前点击的是哪一个
  • leftClickHandle点击左侧是获取右侧的数据,并修改active
  • cates表示左侧的分类
  • secondData表示右侧的分类

在生命周期中执行请求

onLoad () {
     
	this.getPicsCate()
}

写静态的结构

<view class="pics">
	<scroll-view class="left" scroll-y>
		<view 
		@click="leftClickHandle(index,item.id)"
		:class="active===index?'active':''" 
		v-for="(item,index) in cates" 
		:key="item.id">
		  {
    {item.title}}
		view>
	scroll-view>
	<scroll-view class="right" scroll-y>
		<view class="item" v-for="item in secondData" :key="item.id">
			<image @click="previewImg(item.img_url)" :src="item.img_url">image>
			<text>{
    {item.title}}text>
		view>
		<text v-if="secondData.length === 0">暂无数据text>
	scroll-view>
view>
  • 这里使用了一个预览图片的api 详情点击

写样式

page{
     
	height: 100%;
}
.pics{
     
	height: 100%;
	display: flex;
	.left{
     
		width: 200rpx;
		height: 100%;
		border-right:1px solid #eee;
		view{
     
			height: 60px;
			line-height: 60px;
			color: #333;
			text-align: center;
			font-size: 30rpx;
			border-top:1px solid #eee;
		}
		.active{
     
			background: $shop-color;
			color: #fff;
		}
	}
	.right{
     
		height: 100%;
		width: 520rpx;
		margin: 10rpx auto;
		.item{
     
			image{
     
				width: 520rpx;
				height: 520rpx;
				border-radius: 5px;
			}
			text{
     
				font-size: 30rpx;
				line-height: 60rpx;
			}
		}
	}
}

写完之后的效果图
uniapp项目实战笔记_第33张图片

9. 咨讯


打开news.vue

9.1 封装基础组件


根据效果图写静态的页面

这里就不做展示了,直接请求数据,小伙伴们自己写吧,观察效果图,他是一列一列的重复数据,我们可以把它封装成一个组件news-list.vue
uniapp项目实战笔记_第34张图片

接口文档
uniapp项目实战笔记_第35张图片
根据接口文档写方法

news.vue

data() {
     
	return {
     
		newsList: []
	}
},
methods:{
     
	async getNews() {
     
		const res = await this.$myRequest({
     
			url: '/api/getnewslist'
		})
		this.newsList = res.data.message
	},
},

在生命周期中执行方法

onLoad () {
     
	this.getNews()
}

封装静态组件

news-list.vue

<view>
	<view class="news_item" @click="navigator(item.id)" v-for="item in list" :key="item.id">
		<image :src="item.img_url">image>
		<view class="right">
			<view class="tit">
				{
    {item.title}}
			view>
			<view class="info">
				<text>发表时间:{
    {item.add_time | formatDate}}text>
				<text>浏览:{
    {item.click}}text>
			view>
		view>
	view>
view>
  • 注意这里用了一个局部过滤器
  • 定义了一个方法,用于外部触发
	export default {
     
		props: ['list'],
		filters: {
     
			formatDate (date) {
     
				const nDate = new Date(date)
				const year = nDate.getFullYear()
				const month = nDate.getMonth().toString().padStart(2,0)
				const day = nDate.getDay().toString().padStart(2,0)
				return year+'-'+month+'-'+day
			}
		},
		methods:{
     
			navigator (id) {
     
				this.$emit('itemClick',id)
			}
		}
	}
  • 对于padStart的说明,详情点击
.news_item{
     
	display: flex;
	padding: 10rpx 20rpx;
	border-bottom: 1px solid $shop-color;
	image{
     
		min-width: 200rpx;
		max-width: 200rpx;
		height: 150rpx;
	}
	.right{
     
		margin-left: 15rpx;
		display: flex;
		flex-direction: column;
		justify-content: space-between;
		.tit{
     
			font-size: 30rpx;
		}
		.info{
     
			font-size: 24rpx;
			text:nth-child(2){
     
				margin-left: 30rpx;
			}
		}
	}
}

将组件引入news.vue,并传递数据

<template>
	<view class="news">
		<news-item :list="newsList">news-item>
	view>
template>
<script>
	import newsItem from '../../components/news-item/news-item.vue'
	components: {
      "news-item":newsItem},
script>

9.2 跳转到详情页


新建页面

新建一个页面news-detail.vue,根据效果图写静态的页面

接口文档
uniapp项目实战笔记_第36张图片
根据接口文档写方法

data() {
     
	return {
     
		id: 0,
		detail: {
     }
	}
},
methods: {
     
	async getNewsDetail () {
     
		const res = await this.$myRequest({
     
			url: '/api/getnew/'+this.id
		})
		console.log(res)
		this.detail = res.data.message[0]
	}
},

在生命周期中执行方法

onLoad(options){
     
	this.id = options.id
	this.getNewsDetail()
}
  • 因为是一个列表,点击列表的时候一定会传递一个标识,我们从声明周期中拿到标识

写详情页的静态结构

<view class="news_detail">
	<text class="title">{
    {detail.title}}text>
	<view class="info">
		<text>发表时间:{
    {detail.add_time | formatDate}}text>
		<text>浏览:{
    {detail.click}}text>
	view>
	<view class="content">
		<rich-text :nodes="detail.content">rich-text>
	view>
view>
.news_detail{
     
	font-size: 30rpx;
	padding: 0 20rpx;
	.title{
     
		text-align: center;
		width: 710rpx;
		display: block;
		margin: 20rpx 0;
	}
	.info{
     
		display: flex;
		justify-content: space-between;
	}
}

定义全局过滤器
main.js

Vue.filter('formatDate',(date)=>{
     
	const nDate = new Date(date)
	const year = nDate.getFullYear()
	const month = nDate.getMonth().toString().padStart(2,0)
	const day = nDate.getDay().toString().padStart(2,0)
	return year+'-'+month+'-'+day
})

news.vue中定义跳转的方法

<news-item @itemClick="goDetail" :list="newsList">news-item>
goDetail (id) {
     
	uni.navigateTo({
     
		url: '/pages/news-detail/news-detail?id='+id
	})
}

10 打包


详情点击

10.1 小程序打包

10.2 H5打包

10.3 App打包

11 优化

以下内容在分支optimize上,运行命令 git clone -b optimize https://github.com/whynot-todo/uniapp_demo.git

轮播优化

  • 主要优化了图片的展示方式以及指示点,导航的渐变底色

index.vue

image {
     
	height: 370rpx;
	width: 95%;
	border-radius: 10rpx;
	display: block;
	margin: 5rpx auto ;
	background-color: $shop-color;
}
view{
     
	background-image: linear-gradient(to right, #fd0f02,#b50e03);
}
swiper>

禁止滚动条
pages.json

{
     
	"path": "pages/index/index",
	"style": {
     
		"navigationBarTitleText": "商城首页",
		"app-plus":{
     
			"scrollIndicator":"none"
		}
	}
}

App.vue

::-webkit-scrollbar,scrollbar {
     
	display: none !important;
	width: 0px;
	height: 0px;
}

你可能感兴趣的:(微信小程序与uniapp,vue)