利用HBuilder X创建基本项目结构
运行项目
整理基本项目结构,并修改窗口外观
"globalStyle": {
"navigationBarTextStyle": "white",
"navigationBarTitleText": "黑马商城",
"navigationBarBackgroundColor": "#1989fa",
"backgroundColor": "#F8F8F8"
}
创建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"
}
]
}
}
封装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()
定义轮播图的基本结构
<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
黑马超市
联系我们
社区图片
视频专区
.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;
}
}
<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>
.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;
}
}
}
}
定义获取数据的方法
// 获取推荐商品
async getGoods () {
const res = await this.$myRequest({
url: '/api/getgoods?pageindex=1'
})
this.goods = res.data.message
}
在onLoad生命周期中调用该方法
this.getGoods()
通过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>
定义数据
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
})
}
在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
}
使用组件并将数据传递到组件内部
定义获取商品列表数据的方法并调用
<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>
引入商品组件并使用
通过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>
通过onPullDownRefresh进行下拉刷新的操作
onPullDownRefresh() {
this.goods = []
this.pageindex = 1
this.flag = false
setTimeout(()=>{
this.getGoods(()=>{
uni.stopPullDownRefresh()
})
},1000)
}
<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>
<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>
结构
<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;
}
}
}
}
}
创建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>
<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>
点击子组件列表项通过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页面
<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>
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)
}
<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>
<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>
<goods-list @itemClick="godetail" :goods="goods">goods-list>
godetail (item) {
uni.navigateTo({
url: '/pages/goods-detail/goods-detail?id='+item.id
})
}
methods: {
async getDetail(){
const res = await this.$myRequest({
url:'/api/getthumimages/'+this.id
})
console.log(res)
}
},
onLoad(options){
this.id = options.id
this.getDetail()
}
<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>
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()
}
类似于之前小节的实现,这里不再赘述。