uni-app学习记录——2.uni-app搭建一个网上商城

文章目录

  • 1.创建项目 & 清理结构 & 配置基本外观
  • 2.完成底部tabbar的配置
  • 3.获取轮播图数据
  • 4.实现轮播图的结构和数据渲染
  • 5.实现菜单导航结构
    • 5.1 引入字体图标初始化样式
    • 5.2 完成菜单导航基本结构
    • 5.3 菜单导航样式
  • 6.实现推荐商品列表
    • 6.1 定义基本结构
    • 6.2 美化样式
    • 6.3 获取数据
    • 6.4 渲染数据
  • 7.完成超市页面
    • 7.1 改造导航菜单
    • 7.2 创建超市页面
    • 7.3 封装商品列表组件
    • 7.4 渲染商品列表
    • 7.5 实现上拉加载更多
    • 7.6 实现下拉刷新
  • 8.关于我们
  • 9.实现社区图片
  • 10.实现资讯列表
    • 10.1 实现列表项的结构和样式
    • 10.2 封装为组件
    • 10.3 在新闻页面导入并使用
    • 10.4 点击列表进入详情
  • 11.实现资讯详情
    • 11.1 实现基本结构
    • 11.2 获取详情的数据
    • 11.3 实现详情的渲染
  • 12.实现商品详情页
    • 12.1 商品列表注册点击事件
    • 12.2 父组件绑定自定义事件进行跳转
    • 12.3 创建商品详情页
    • 12.4 获取详情轮播图数据
    • 12.5 渲染轮播图
    • 12.6 获取商品信息
    • 12.7 完成商品信息结构和渲染

1.创建项目 & 清理结构 & 配置基本外观

  • 利用HBuilder X创建基本项目结构

  • 运行项目

  • 整理基本项目结构,并修改窗口外观

    "globalStyle": {
      "navigationBarTextStyle": "white",
      "navigationBarTitleText": "黑马商城",
      "navigationBarBackgroundColor": "#1989fa",
      "backgroundColor": "#F8F8F8"
    }
    

2.完成底部tabbar的配置

  • 创建tabbar对应的四个页面和图标准备好

  • 将页面路径配置到pages.json中的pages数组中

    "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
    		{
    			"path": "pages/index/index"
    		},
    		{
    			"path": "pages/member/member"
    		},
    		{
    			"path": "pages/cart/cart"
    		},
    		{
    			"path": "pages/search/search"
    		}
    	]
    
  • 配置tabbar

    {
    	"tabBar": {
    		"list": [
    			{
    				"pagePath":"pages/index/index",
    				"text":"首页",
    				"iconPath":"static/icon/home.png",
    				"selectedIconPath":"static/icon/home-active.png"
    			},
    			{
    				"pagePath":"pages/member/member",
    				"text":"会员",
    				"iconPath":"static/icon/member.png",
    				"selectedIconPath":"static/icon/member-active.png"
    			},
    			{
    				"pagePath":"pages/cart/cart",
    				"text":"购物车",
    				"iconPath":"static/icon/cart.png",
    				"selectedIconPath":"static/icon/cart-active.png"
    			},
    			{
    				"pagePath":"pages/search/search",
    				"text":"搜索",
    				"iconPath":"static/icon/search.png",
    				"selectedIconPath":"static/icon/search-active.png"
    			}
    		]
    		
    	}
    }
    

3.获取轮播图数据

  • 封装uni.request请求,并挂在到全局

    • 创建util》api.js

      // 封装get请求
      const baseUrl = "http://localhost:8082"
      export const myRequest = (options)=>{
      	return new Promise((resolve,reject)=>{
      		uni.request({
      			method: options.method,
      			data: options.data,
      			url: baseUrl+options.url,
      			success(res) {
      				if(res.data.status !== 0) {
      					return uni.showToast({
      						title: '获取数据失败'
      					})
      				}
      				resolve(res)
      			},
      			fail(err) {
      				uni.showToast({
      					title: '获取数据失败'
      				})
      				reject(err)
      			}
      		})
      	})
      }
      
    • 在main.js中导入并挂载到全局

      import { myRequest } from './util/api.js'
      Vue.prototype.$myRequest = myReques
      
  • 获取轮播图的数据

    • 定义获取轮播图的方法

      methods: {
        async getSwipers () {
          const res = await this.$myRequest({
            method: 'GET',
            url: '/api/getlunbo'
          })
          this.swipers = res.data.message
        }
      }
      
    • 在onLoad中调用该方法

      this.getSwipers()
      

4.实现轮播图的结构和数据渲染

  • 定义轮播图的基本结构

    <swiper class="swiper" indicator-dots :autoplay="true" :interval="2000" circular>
      <swiper-item v-for="item in swipers" :key="item.id">
        <image :src="item.img">image>
      swiper-item>
    swiper>
    
  • 样式,在工具中安装scss

    
    

5.实现菜单导航结构

5.1 引入字体图标初始化样式


5.2 完成菜单导航基本结构


			
				
				黑马超市
			
			
				
				联系我们
			
			
				
				社区图片
			
			
				
				视频专区
			
		

5.3 菜单导航样式

.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;
  }
}

6.实现推荐商品列表

6.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>

6.2 美化样式

.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;

      }
    }
  }
}

6.3 获取数据

  • 定义获取数据的方法

    // 获取推荐商品
    async getGoods () {
      const res = await this.$myRequest({
        url: '/api/getgoods?pageindex=1'
      })
      this.goods = res.data.message
    }
    
  • 在onLoad生命周期中调用该方法

    this.getGoods()
    

6.4 渲染数据

  • 通过v-for渲染数据

    <view class="hot_goods">
      <view class="tit">推荐商品view>
      
      <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>
    view>
    

7.完成超市页面

7.1 改造导航菜单

  • 定义数据

    navs: [
      {
        icons: "iconfont icon-ziyuan",
        title: "黑马超市",
        path: "/pages/goods/list"
      },
      {
        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"
      }
    ]
    
  • 渲染数据

    <view class="nav">
      <view class="item" v-for="(item,index) in navs" :key="index">
        <view :class="item.icons">view>
        <text>{{item.title}}text>
      view>
    view>
    
  • 给导航菜单注册点击事件

    <view class="goods_item" v-for="item in goods" :key="item.id">
    
  • 定义跳转的方法

    goNavigator (url) {
      uni.navigateTo({
        url
      })
    }
    

7.2 创建超市页面

  • 创建页面,goods>list.vue
  • 将页面路劲配置到pages文件中,修改标题

7.3 封装商品列表组件

  • 在components下面创建goods>list.vue

    <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>
    template>
    
    <script>
    	export default {
    		props:{
    			goods:Array
    		}
    	}
    script>
    
    <style lang="scss">
    	.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: 150px;
    				display: block;
    				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;
    				
    			}
    		}
    	}
    style>
    
  • 在首页引入该组件

    import goodsList from "../../components/goods-list/index.vue"
    
    components: {
      "goods-list":goodsList
    }
    
  • 使用组件并将数据传递到组件内部

    
    

7.4 渲染商品列表

  • 定义获取商品列表数据的方法并调用

    <script>
    	export default {
    		data () {
    			return {
    				goods: []
    			}
    		},
    		methods: {
    			async getGoods () {
    				const res = await this.$myRequest({
    					url: '/api/getgoods?pageindex=1'
    				})
    				this.goods = res.data.message
    			},
    		},
    		
    		onLoad () {
    		    this.getGoods()
    		}
    	}
    </script>
    
  • 引入商品组件并使用

    
    
    
    

7.5 实现上拉加载更多

  • 通过onReachBottom来监听触底

    onReachBottom () {
      this.pageindex++
      this.getGoods()
    }
    
  • 修改给goods赋值

    this.goods = [...this.goods,...res.data.message]
    
  • 动态显示底线

    • 通过onReachBottom监听是否还有更多

      if(this.pageindex*10>this.goods.length) return this.flag = true
      
    • 通过v-if控制底线

      <view class="over_line" v-if="flag">----------我是有底线的----------</view>
      

7.6 实现下拉刷新

  • 通过onPullDownRefresh进行下拉刷新的操作

    onPullDownRefresh() {
      this.goods = []
      this.pageindex = 1
      this.flag = false 
      setTimeout(()=>{
        this.getGoods(()=>{
          uni.stopPullDownRefresh()
        })
      },1000)
    }
    

8.关于我们

<template>
	<view class="contact">
		<image class="img" src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3252521864,872614242&fm=26&gp=0.jpg">image>
		<view class="info">
			<view @click="phone">联系电话:123-456-789(点击拨打)view>
			<view>联系地址:CSDN-赈川view>
		view>
		<map class="map" v-bind:longitude="longitude" v-bind:latitude="latitude" v-bind:markers="markers">map>
		联系我们
	view>
template>

<script>
	export default {
		data() {
			return {
				longitude : 108.910649,
				latitude: 34.158268,
				markers:[
					{
						longitude : 108.910649,
						latitude: 34.158268,
						iconPath : '../../static/logo.png'
					}
				]
			}
		},
		methods: {
			phone(){
				uni.makePhoneCall({
					phoneNumber: '123-456-789'
				})
			}
		}
	}
script>

<style lang="scss">
	.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;
		}
	}
style>

9.实现社区图片

<template>
	<view class="pics">
		<scroll-view class="left" scroll-y="true">
			<view @click="leftClickHandle(index,item.id)" v-bind:class="active==index?'active':''" v-bind="active" v-for="(item,index) in cates" v-bind:key="item.id">
				{{item.title}}
			view>
		scroll-view>
		<scroll-view class="right" scroll-y="true">
			<view class="item" v-for="item in secondData" v-bind:key="item.id">
				<image @click="previewImg(item.img_url)" v-bind:src="item.img_url">image>
				<text>{{item.title}}text>
			view>
			<text v-if="secondData.length === 0">暂无数据text>
		scroll-view>
	view>
template>

<script>
	export default {
		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;
			},
			previewImg(current){
				const urls = this.secondData.map(item => {
					return item.img_url;
				})
				uni.previewImage({
					current,
					urls
				})
			}
		},
		onLoad(){
			this.getPicsCate()
		}
	}
script>

<style lang="scss">
	// 适配微信小程序
	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;
				}
			}
		}
		
	}
style>

10.实现资讯列表

10.1 实现列表项的结构和样式

结构

<view class="news_item">
  <image src="../../static/logo.png">image>
  <view class="content">
    <view class="tit">1季度多家房企利润跌幅超50% 去库存促销战打响view>
    <view class="info">
      <text>发表时间:2019-12-23text>
      <text>浏览:1次text>
    view>
  view>
view>

样式

.news{
	.news_item{
		display: flex;
		padding: 5px 10px;
		border-bottom: 3px solid $shop-color;
		image{
			width: 300rpx;
			height: 150rpx;
		}
		.content {
			padding: 5px 10px;
			position: relative;
			.tit{
				font-size: 30rpx;
			}
			.info{
				font-size: 26rpx;
				position: absolute;
				left: 10px;
				bottom: 5px;
				text:nth-child(2){
					margin-left: 20px;
				}
			}
		}
	}
}

10.2 封装为组件

创建news-item.vue

<template>
	<view>
		<view class="news_item" v-for="item in data" :key="item.id">
			<image :src="item.img_url">image>
			<view class="content">
				<view class="tit">{{item.title}}view>
				<view class="info">
					<text>发表时间:{{item.add_time | formatDate}}text>
					<text>浏览:{{item.click+123}}次text>
				view>
			view>
		view>
	view>
template>

<script>
	export default {
		props: ['data'],
		filters:{
		  formatDate(data){
				const date = new Date(data)
				console.log(date)
				const day = date.getMonth().toString().padStart(2,'0')+'-'+date.getDay().toString().padStart(2,'0')
				return date.getFullYear()+'-'+day
			}	
		}
	}
script>

<style lang="scss">
	.news_item{
		display: flex;
		padding: 5px 10px;
		border-bottom: 1rpx solid $shop-color;
		image{
			max-width: 200rpx;
			min-width: 200rpx;
			height: 150rpx;
		}
		.content {
			padding: 5px 10px;
			position: relative;
			.tit{
				font-size: 30rpx;
			}
			.info{
				font-size: 26rpx;
				position: absolute;
				left: 10px;
				bottom: 5px;
				text:nth-child(2){
					margin-left: 20px;
				}
			}
		}
	}
style>

10.3 在新闻页面导入并使用

<template>
	<view class="news">
		<new-item :data="newsList">new-item>
	view>
template>
<script>
	import newItem from '../../components/new-item/new-item.vue'
	export default {
		data() {
			return {
				newsList: []
			}
		},
		methods: {
			async getNewsList () {
				const res = await this.$myRequest({
					url:'/api/getnewslist'
				})
				this.newsList = res.data.message
			}
		},
		onLoad () {
			this.getNewsList()
		},
		components: {
			"new-item":newItem
		}
	}
script>

<style lang="scss">
style>

10.4 点击列表进入详情

  • 点击子组件列表项通过this.$emit触发父组件的方法

    navigatorTo (item) {
      this.$emit('clickItem',item)
    }
    
  • 父组件通过注册clickItem事件及事件函数实现跳转操作

    <template>
    	<view class="news">
    		<new-item :data="newsList" @clickItem="goDetail">new-item>
    	view>
    template>
    <script>
    	export default {
    		methods: {
    			goDetail (data) {
    				console.log(data)
    				uni.navigateTo({
    					url: '/pages/news-detail/news-detail'
    				})
    			}
    		}
    	}
    script>
    
  • 新建/pages/news-detail/news-detail页面

11.实现资讯详情

11.1 实现基本结构

<template>
	<view class="news_detail">
		<view class="news_title">
			标题
		view>
		<view class="info">
			<text>发表时间:text>
			<text>浏览:text>
		view>
		<view class="content">
			
		view>
	view>
template>

<script>
	export default {
		data() {
			return {
				
			}
		},
		methods: {
			
		}
	}
script>

<style lang="scss">
.news_detail {
	padding: 15rpx;
	.news_title{
		text-align: center;
		font-size: 32rpx;
	}
	.info{
		font-size: 28rpx;
		display: flex;
		justify-content: space-between;
	}
}
style>

11.2 获取详情的数据

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

  onLoad (options){
    this.getNewsDetail(options.id)
  }

11.3 实现详情的渲染

<view class="news_title">
  {{newsDetail.title}}
    </view>
<view class="info">
  <text>发表时间:{{newsDetail.add_time | formatDate}}</text>
<text>浏览:{{newsDetail.click}}</text>
</view>
<view class="content">
  <rich-text :nodes="newsDetail.content"></rich-text>
</view>

12.实现商品详情页

12.1 商品列表注册点击事件

<template>
	<view class="goods_list">
		<view @click="itemClick(item)" class="goods_item" v-for="item in goods" :key="item.id">
		</view>
	</view>
</template>
<script>
	export default {
		props:{
			goods:Array
		},
		methods: {
			itemClick(item) {
				this.$emit('itemClick',item)
			}
		}
	}
</script>

12.2 父组件绑定自定义事件进行跳转

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

12.3 创建商品详情页

12.4 获取详情轮播图数据

methods: {
  async getDetail(){
    const res = await this.$myRequest({
      url:'/api/getthumimages/'+this.id
    })
    console.log(res)
  }
},
onLoad(options){
    this.id = options.id
    this.getDetail()
}

12.5 渲染轮播图

<swiper indicator-dots>
  <swiper-item v-for="item in swipers" :key="item.src">
    <image :src="item.src">image>
  swiper-item>
swiper>
<style lang="scss">
.goods_detail{
	swiper{
		height: 700rpx;
		image{
			width: 100%;
			height: 100%;
		}
	}
}
style>

12.6 获取商品信息

methods: {
  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()
}

12.7 完成商品信息结构和渲染

类似于之前小节的实现,这里不再赘述。

你可能感兴趣的:(Uniapp,uniapp,h5app,微信小程序,android)