项目源码地址:https://github.com/whynot-todo/uniapp_demo.git
进行项目的搭建,详情点击
pages.json
,在globalStyle
下配置全局的样式,你可以参照官网来进行配置,详情点击"globalStyle": {
"navigationBarTextStyle": "white",
"navigationBarTitleText": "商城",
"navigationBarBackgroundColor": "##b50e03",
"backgroundColor": "#F8F8F8"
}
配置好后是这个样子的,因为局部配置的优先级较高,所以navigationBar
显示的是uniapp
1. 新建页面
根据效果图,tabber联系着四个页面,分别是首页、咨询、购物车、会员,我们新建四个页面分别是
pages/index/index
pages/cart/cart
pages/member/member
pages/news/news
2. 新建页面步骤
scss
pages.json
中注册路径,这里的四个我们都需要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
数组的第一个元素表示启动时的默认页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端需要做跨域的处理,详情点击,我们这里主要写小程序,所以就不处理了。mian.js
=> 引入组件 => 将方法添加到原形中import {
myRequest} from 'utils/api.js'
Vue.prototype.$myRequest = myRequest
phpStudy
,并打开,这里使用的版本为8.1.0.1打开
,点击SQL_Front
localhost
,选择新建
,选择数据库
node app.js
,将后台的项目运行起来。navigationBarTitleText
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "商城首页"
}
}
获取轮播图数据
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 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{
width: 750rpx;
height: 380rpx;
image{
height: 100%;
width: 100%;
}
}
说明
indicator-dots
为指示小圆点circular
循环滑动autoplay
自动轮播indicator-color
原点背景颜色1. 静态的导航
素材/font_nav
将我们下载好的字体图标放到static/fonts
文件夹下,我们只需要其中的五个文件
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";
}
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>
uni.scss
中定义一个主题颜色,如果使用的是less
我们可以新建一个全局文件定义变量/* 颜色变量 */
$shop-color: #b50e03;
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;
}
}
当我们配置完后,就会出现以下画面
我们为四个导航按钮新建四个页面,上面有配置页面的步骤在搭建框架
/pages/goods/goods
/pages/pics/pics
/pages/contact/contact
/pages/videos/videos
我们需要为四个图标定义点击跳转事件,我们需要用到uni.navigateTo(OBJECT)
详情点击
优化导航结构
index.vue
的data(){}
中,添加如下配置数据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"
}
]
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>
methods
中定义点击的方法navItemClick(url) {
uni.navigateTo({
url
})
}
单个的静态结构
<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;
}
}
}
}
商品组件
components/goods-list
文件夹,并新建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>
index.vue
中引入并映射成组件import GoodsList from '../../components/goods-list/goods-list.vue'
components:{
GoodsList
}
<view class="hot_goods">
<view class="tit">推荐商品view>
<goods-list>goods-list>
view>
获取动态的数据
index.vue
,methods
中定义方法获取数据methods
// 获取热门商品列表数据
async getHotGoods () {
const res = await this.$myRequest({
url: '/api/getgoods?pageindex=1'
})
this.goods = res.data.message
},
data
data(){
return {
goods: []
}
}
onLoad() {
this.getHotGoods()
},
将数据传递到子组件标签
<goods-list :goods="goods">goods-list>
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>
效果
我们新建一个页面为goods-detail
绑定点击监听
为每一个商品绑定点击监听
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)
}
}
在生命周期中得到,传来的数据
得到相应的数据,并在data中保存
data() {
return {
id: 0,
},
onLoad (options) {
this.id = options.id
}
获取展示轮播图数据
我们观察到展示的商品图片是一个轮播的形式,我们可以先写静态组件,这样也是比较科学,这里就不展示了,直接获取数据写结构
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%;
}
}
当我们把上面写好之后,详情轮播图已经写好了。
获取商品信息
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;
}
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;
}
}
当我们将上面的代码写完的时候,就会出现以下效果图
我们发现整个图片没有完全显示,我们观看以下打印的数据,发现每个图片上都有一个类属性gomeImgLoad
,我们给他一个宽度,一定要注意放在全局样式中App.vue
.gomeImgLoad{
width: 750rpx;
height: auto;
}
再次刷新,我们发现已经好了
寻找组件
2. 点开网页之后,我们导入组件,点击使用HBuilderX导入组件
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++
}
}
}
goods-detail.vue
中写上如下代码.goods_nav {
position: fixed;
bottom:0;
width: 100%;
}
这里的商品展示其实是和我们的首页的展示是差不多的,我们就可以利用首页的组件来进行作品的展示。
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>
触底加载属于生命周期函数,详情点击
获取数据
获取数据。与首页不同的是,首页只获取了一页数据,而这里到达底部之后会再次获取数据
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>
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;
}
详情点击
pages.json
,我们顺着可以改一下导航的文字"style": {
"navigationBarTitleText": "商品列表",
"enablePullDownRefresh": true
}
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()
},
在我们写首页数据的时候,在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
})
}
打开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
详情点击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
他是用来打电话的,详情点击
打开pics.vue
,观察效果图,我们发现他的左右两个部分,都属于滚动的部分,这就需要我们的组件来控制scroll-view
,详情点击
我们可以将静态的组件写好,再去写动态的组件,这里就不演示了,直接从后台获取数据
接口文档
写请求的方法
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>
写样式
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;
}
}
}
}
打开news.vue
根据效果图写静态的页面
这里就不做展示了,直接请求数据,小伙伴们自己写吧,观察效果图,他是一列一列的重复数据,我们可以把它封装成一个组件news-list.vue
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>
新建页面
新建一个页面news-detail.vue
,根据效果图写静态的页面
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
})
}
详情点击
以下内容在分支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;
}