UNIAPP实战项目笔记55 自定义Tabbar并使用Tabbar拦截未登录跳转到登录页面
点击购物车和我的的时候需要拦截并验证登录
通过验证的直接跳转,为通过验证的跳转到登录页面
通过自定义tabbar来实现
实际案例图片
-
正常跳转
-
拦截跳转
代码 pages.json
页面配置
{
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "百年奥莱",
"navigationBarBackgroundColor": "#fff",
// "navigationStyle": "custom",
"app-plus": {
"scrollIndicator": "none",
"titleNView": {
/* 使用字体图标时 unicode 字符表示必须 '\u' 开头,如 "\ue123"(注意不能写成"\e123")*/
"buttons": [
{
"float": "left",
"fontSrc": "./static/iconfont/iconfont.ttf",
"text": "\ue616"
},{
"float": "right",
"fontSrc": "./static/iconfont/iconfont.ttf",
"text": "\ue711"
}
]
}
}
}
}
,{
"path" : "pages/my/my",
"style" :
{
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#fff",
"app-plus": {
"titleNView": false,
"scrollIndicator": "none"
}
}
}
,{
"path" : "pages/shopcart/shopcart",
"style" :
{
"app-plus": {
"titleNView": false,
"scrollIndicator": "none"
},
"mp-weixin": {
"navigationStyle": "custom"
}
}
}
,{
"path" : "pages/list/list",
"style" :
{
"navigationBarBackgroundColor": "#FFFFFF",
"app-plus": {
"scrollIndicator": "none",
"titleNView": {
"searchInput": {
"placeholder": "请输入关键字",
"disabled": true,
"align": "left",
"autoFocus": false,
"borderRadius": "15px",
"backgroundColor": "#F7F7F7",
"placeholderColor": "#B3B3B3"
}
}
},
"mp-weixin": {
"navigationBarTitleText": "商品分类"
}
}
}
,{
"path" : "pages/search/search",
"style" :
{
"navigationBarBackgroundColor": "#FFFFFF",
"app-plus": {
"scrollIndicator": "none",
"titleNView": {
"searchInput": {
"placeholder": "nike",
"disabled": false,
"align": "left",
"autoFocus": true,
"borderRadius": "15px",
"backgroundColor": "#F7F7F7",
"placeholderColor": "#B3B3B3"
},
"buttons": [
{
"float": "right",
"color": "#636263",
"text": "搜索",
"fontSize": "16px",
"width":"60px"
}
]
}
}
}
}
,{
"path" : "pages/search-list/search-list",
"style" :
{
"navigationBarBackgroundColor": "#FFFFFF",
"app-plus": {
"scrollIndicator": "none",
"titleNView": {
"searchInput": {
"placeholder": "nike",
"disabled": true,
"align": "left",
"autoFocus": false,
"borderRadius": "15px",
"backgroundColor": "#F7F7F7",
"placeholderColor": "#B3B3B3"
},
"buttons": [
{
"float": "right",
"color": "#636263",
"text": "筛选",
"fontSize": "16px",
"width":"60px"
}
]
}
}
}
}
,{
"path" : "pages/details/details",
"style" :
{
"navigationBarTitleText": "商品详情",
"app-plus": {
"scrollIndicator": "none",
"titleNView": {
"type": "transparent",
"buttons": [
{
"type": "menu"
},
{
"type": "share"
}
]
}
}
}
}
,{
"path" : "pages/my-config/my-config",
"style" :
{
"navigationBarBackgroundColor": "#FFFFFF",
"navigationBarTitleText": "设置",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/my-path-list/my-path-list",
"style" :
{
"navigationBarBackgroundColor": "#FFFFFF",
"navigationBarTitleText": "地址管理",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/my-add-path/my-add-path",
"style" :
{
"navigationBarBackgroundColor": "#FFFFFF",
"navigationBarTitleText": "新增地址",
"app-plus": {
"scrollIndicator": "none",
"titleNView": {
"buttons": [
{
"float": "right",
"color": "#636263",
"text": "保存",
"fontSize": "16px",
"width":"60px"
}
]
}
}
}
}
,{
"path" : "pages/my-order/my-order",
"style" :
{
"navigationBarBackgroundColor": "#FFFFFF",
"navigationBarTitleText": "我的订单",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/confirm-order/confirm-order",
"style" :
{
"navigationBarBackgroundColor": "#FFFFFF",
"navigationBarTitleText": "确认订单",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/qianzi/qianzi",
"style" :
{
"navigationBarTitleText": "签字",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/payment/payment",
"style" :
{
"app-plus": {
"titleNView": false,
"scrollIndicator": "none"
}
}
}
,{
"path" : "pages/payment-success/payment-success",
"style" :
{
"navigationBarTitleText": "支付成功",
"enablePullDownRefresh": false,
"navigationBarBackgroundColor": "#fff"
}
}
,{
"path" : "pages/login/login",
"style" :
{
"app-plus": {
"titleNView": false,
"scrollIndicator": "none"
}
}
}
,{
"path" : "pages/login-tel/login-tel",
"style" :
{
"navigationBarTitleText": "输入手机号",
"navigationBarBackgroundColor": "#fff"
}
}
,{
"path" : "pages/login-code/login-code",
"style" :
{
"navigationBarTitleText": "输入验证码",
"navigationBarBackgroundColor": "#fff"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
}
// "tabBar": {
// "color": "#636263",
// "selectedColor": "#636263",
// "backgroundColor": "#fff",
// "spacing": "5px",
// "list": [
// {
// "pagePath": "pages/index/index",
// "iconPath": "static/tabbar/index.png",
// "selectedIconPath": "static/tabbar/indexSelected.png",
// "text": "首页"
// },{
// "pagePath": "pages/list/list",
// "iconPath": "static/tabbar/list.png",
// "selectedIconPath": "static/tabbar/listSelected.png",
// "text": "分类"
// },{
// "pagePath": "pages/shopcart/shopcart",
// "iconPath": "static/tabbar/shop.png",
// "selectedIconPath": "static/tabbar/shopSelected.png",
// "text": "购物车"
// },{
// "pagePath": "pages/my/my",
// "iconPath": "static/tabbar/index.png",
// "selectedIconPath": "static/tabbar/indexSelected.png",
// "text": "我的"
// }
// ]
// }
}
代码 main.js
主配置文件
增加 权限跳转 代码段 用于拦截未登录的跳转
import App from './App'
// #ifndef VUE3
import Vue from 'vue'
Vue.config.productionTip = false
// 引入状态机
import store from 'store/index.js'
Vue.prototype.$store = store;
// 权限跳转,未登录的跳转到登录
Vue.prototype.navigateTo = (options)=>{
if (!store.state.user.loginStatus) {
uni.showToast({
title:"请先登录",
icon:'none'
})
uni.navigateTo({
url:"/pages/login/login"
})
return
}
uni.navigateTo(options);
}
App.mpType = 'app'
const app = new Vue({
...App,
store // 挂载状态机
})
app.$mount()
// #endif
// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}
// #endif
代码 Tabbar.vue组件
自定义的Tabbar组件页面
购物车和我的页面单独设置需要跳转验证
<template>
<view class="tabbar">
<view class="tab"
v-for="(item,index) in tabbarList"
:key="index"
@tap="navigatorTo(item.pagePath)"
>
<image class="tabimg" v-if="item.pagePath === currentPage " :src="item.selectedIconPath" mode=""></image>
<image class="tabimg" v-else :src="item.iconPath" mode=""></image>
<view class="text">{{item.text}}</view>
</view>
</view>
</template>
<script>
export default {
props:{
currentPage:{
type:String,
default:'index'
}
},
data() {
return {
tabbarList:[
{
"pagePath": "index",
"iconPath": "/static/tabbar/index.png",
"selectedIconPath": "/static/tabbar/indexSelected.png",
"text": "首页"
},{
"pagePath": "list",
"iconPath": "/static/tabbar/list.png",
"selectedIconPath": "/static/tabbar/listSelected.png",
"text": "分类"
},{
"pagePath": "shopcart",
"iconPath": "/static/tabbar/shop.png",
"selectedIconPath": "/static/tabbar/shopSelected.png",
"text": "购物车"
},{
"pagePath": "my",
"iconPath": "/static/tabbar/my.png",
"selectedIconPath": "/static/tabbar/mySelected.png",
"text": "我的"
}
]
};
},
methods:{
navigatorTo(e){
if( e=='shopcart' || e=='my' ){
let u1 = `/pages/${e}/${e}`;
console.log(u1);
this.navigateTo({
url:u1,
animationType:'fade-in',
animationDuration:0
})
}else{
let u2 = `/page/${e}/${e}`;
console.log(u2);
uni.reLaunch({
url: `/pages/${e}/${e}`,
});
}
}
}
}
</script>
<style lang="scss">
.tabbar{
border-top: 2rpx solid #636263;
background-color: #fff;
z-index: 9999;
position: fixed;
left: 0;
bottom: 0;
width: 100%;
height: 120rpx;
display: flex;
justify-content: space-around;
align-items: center;
}
.tab{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.tabimg{
width: 40rpx;
height: 40rpx;
}
.text{
font-size: 24rpx;
}
</style>
代码 index.vue页面
首页页面,引入自定义组件Tabbar
<template>
<view class="content">
<view class="index">
<scroll-view scroll-x="true" :scroll-into-view="scrollIntoIndex" class="scroll-content" >
<view
:id="'top'+index"
class="scroll-item"
v-for="(item,index) in topBar"
:key="index"
@tap="changeTab(index)"
>
<text :class="topBarIndex===index?'f-active-color':'f-color'">{{item.name}}</text>
</view>
</scroll-view>
<swiper @change="onChangeTab" :current="topBarIndex" :style="'height:'+clentHeight+'px'">
<swiper-item
v-for="(item,index) in newTopBar"
:key="index"
>
<scroll-view @scrolltolower="loadMore(index)" scroll-y="true" :style="'height:'+clentHeight+'px;'">
<block v-if="item.data.length > 0">
<block v-for="(k,i) in item.data" :key="i">
<!-- 推荐 -->
<IndexSwiper v-if="k.type==='swiperList'" :dataList='k.data'></IndexSwiper>
<template v-if="k.type==='recommendList'">
<Recommend :dataList='k.data'></Recommend>
<Card cardTitle='猜你喜欢'></Card>
<!-- 卡片要在下面显示,但是位置要放到上面 template 表示一个整体循环 -->
</template>
<!-- 运动户外... -->
<Banner v-if="k.type==='bannerList'" :dataList='k.imgUrl'></Banner>
<template v-if="k.type==='iconsList'">
<Icons :dataList='k.data'></Icons>
<Card cardTitle="热销商品"></Card>
</template>
<template v-if="k.type==='hotList'">
<Hot :dataList='k.data'></Hot>
<Card cardTitle="推荐店铺"></Card>
</template>
<template v-if="k.type==='shopList'">
<Shop :dataList='k.data'></Shop>
<Card cardTitle="为您推荐"></Card>
</template>
<CommodityList v-if="k.type==='commodityList'" :dataList='k.data'></CommodityList>
</block>
</block>
<view v-else>
暂无数据...
</view>
<view class="load-text f-color">
{{item.loadText}}
</view>
</scroll-view>
</swiper-item>
</swiper>
<!-- 推荐模板 -->
<!-- <IndexSwiper></IndexSwiper>
<Recommend></Recommend>
<Card cardTitle='猜你喜欢'></Card>
<CommodityList></CommodityList> -->
<!-- 其他模板: 运动户外 美妆... -->
<!-- <Card cardTitle='运动户外'></Card>
<Banner></Banner>
<Icons></Icons>
<Card cardTitle='热销爆品'></Card>
<Hot></Hot>
<Card cardTitle='店铺推荐'></Card>
<Shop></Shop>
<Card cardTitle="为您推荐"></Card>
<CommodityList></CommodityList>
-->
</view>
<!-- <view class="f-active-color">
文字
</view>
<view class="iconfont icon-xiaoxi"></view>
<view class="text-area">
<text class="title">{{title}}</text>
</view> -->
<Tabbar currentPage='index'></Tabbar>
</view>
</template>
<script>
import $http from '@/common/api/request.js'
import IndexSwiper from '@/components/index/indexSwiper.vue';//引入
import Recommend from '@/components/index/Recommend.vue';//引入
import Card from '@/components/common/Card.vue';//引入
import CommodityList from '@/components/common/CommodityList.vue';//引入
import Banner from '@/components/index/Banner.vue';//引入
import Icons from '@/components/index/Icons.vue';//引入
import Hot from '@/components/index/Hot.vue';//引入
import Shop from '@/components/index/Shop.vue';//引入
import Tabbar from '@/components/common/Tabbar.vue';//引入
export default {
data() {
return {
// 选中的索引
topBarIndex:0,
// 顶栏跟随的索引id值
scrollIntoIndex:'top0',
// 内容块的高度
clentHeight:0,
// 顶栏数据
topBar:[],
// 承载数据
newTopBar:[],
title: 'Hello'
}
},
components:{
IndexSwiper, //注册
Recommend,
Card,
CommodityList,
Banner,
Icons,
Hot,
Shop,
Tabbar
},
onLoad() {
// 请求接口数据
this.__init();
},
// 标题栏按钮点击
onNavigationBarButtonTap(e) {
if(e.float == 'left'){
uni.navigateTo({
url:'/pages/search/search'
})
}
},
onReady() { // 初步渲染完后执行
uni.getSystemInfo({
success: (res) => {
// console.log(res);
// 可视区域高度 减去头部高度
// this.clentHeight = res.windowHeight - uni.upx2px(80) - this.getClientHeight();
this.clentHeight = res.windowHeight - this.getClientHeight();
}
})
/* let view = uni.createSelectorQuery().select(".home-data"); // 获取dom节点对象
// 获取节点对象数据
view.boundingClientRect(data=>{
// 动态获取内容块的高度,动态渲染swiper高度
// 不要去试图计算可视区域的高度,在ios下有bug
this.clentHeight = 2000;
// this.clentHeight = data.height;
}).exec(); */
// this.getClientHeight();
},
methods: {
// 请求首页数据
__init(){
$http.request({
url:'/index_list/data'
}).then((res)=>{
this.topBar = res.topBar;
this.newTopBar = this.initData(res);
}).catch(()=>{
uni.showToast({
title:'请求失败',
icon:'none'
})
})
},
// 添加数据
initData(res){
let arr = [];
for (var i = 0; i < this.topBar.length; i++) {
let obj = {
data:[],
load:"first",
loadText:"上拉加载更多..."
}
// 获取首次数据
if (i==0) {
obj.data = res.data
}
arr.push(obj)
}
return arr;
},
// 点击顶栏
changeTab(index){
if (this.topBarIndex === index) {
return;
}
this.topBarIndex = index
this.scrollIntoIndex = 'top'+index
// 每一次滑动 load赋值为first
if(this.newTopBar[this.topBarIndex].load == 'first'){
this.addData();
}
},
// 对应滑动
onChangeTab(e){
this.changeTab(e.detail.current)
},
// 获取可视区域高度【兼容】
getClientHeight(){
const res = uni.getSystemInfoSync();
console.log(res.platform,res.statusBarHeight);
const system = res.platform;
if ( system === 'iso') {
return 44 + res.statusBarHeight;
}else if( system === 'android' ){
return 48 + res.statusBarHeight;
} else{
return 0;
}
},
// 对应显示不同数据
addData(callback){
// 拿到索引
let index = this.topBarIndex;
// 拿到id
let id = this.topBar[index].id;
// 计算页码
let page = Math.ceil(this.newTopBar[index].data.length/5) + 1;
// console.log(page);
// 请求不同的数据
$http.request({
url:`/index_list/${id}/data/${page}`
}).then((res)=>{
this.newTopBar[index].data = [...this.newTopBar[index].data,...res]
}).catch(()=>{
uni.showToast({
title:'请求失败',
icon:'none'
})
})
// 请求结束后重新赋值
this.newTopBar[index].load = 'last';
if(typeof callback === 'function'){
callback();
}
},
// 上拉加载更多
loadMore(index){
this.newTopBar[index].loadText = '加载中...';
// 请求完数据,文字提示信息又换成【上拉加载更多...】
this.addData(()=>{
this.newTopBar[index].loadText = '上拉加载更多...';
})
}
}
}
</script>
<style>
.load-text{
border-top: 2rpx solid #636263;
line-height: 60rpx;
text-align: center;
}
.scroll-content{
width: 100%;
height: 80rpx;
white-space: nowrap;
}
.scroll-item{
display: inline-block;
padding: 10rpx 30rpx;
font-size: 32rpx;
}
.f-active-color{
padding: 10rpx 0;
border-bottom: 6rpx solid #49BDFB;
}
.index{
width: 100%;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
</style>
代码 list.vue页面
商品列表页面,引入自定义组件Tabbar
<template>
<view class="">
<Lines />
<view class="list">
<!-- 左侧滑动 -->
<scroll-view scroll-y="true" class="list-left" :style="'height:'+clentHeight+'px'">
<view
v-for="(item,index) in leftData"
@tap="changeLeftTab(index,item.id)"
class="left-item"
>
<view
class= "left-name"
:class='activeIndex===index?"left-name-active":""'
>{{item.name}}</view>
</view>
</scroll-view>
<!-- 右侧滑动 -->
<scroll-view scroll-y="true" class="list-right" :style="'height:'+clentHeight+'px'">
<view
class="right-list"
v-for="(item,index) in rightData"
:key="index"
>
<block v-for="(k,i) in item">
<view class="list-title">{{k.name}}</view>
<view class="right-content">
<view class="right-item" v-for="(j,idx) in k.list">
<image class="right-img" :src="j.imgUrl" mode=""></image>
<view class="right-name">
{{j.name}}
</view>
</view>
</view>
</block>
</view>
</scroll-view>
</view>
<Tabbar currentPage='list'></Tabbar>
</view>
</template>
<script>
import $http from '@/common/api/request.js'
import Lines from "@/components/common/Lines.vue"
import Tabbar from '@/components/common/Tabbar.vue';//引入
export default {
data() {
return {
clentHeight:0,
activeIndex:0,
// 左侧数据
leftData:[],
rightData:[],
}
},
components:{
Lines,
Tabbar
},
onLoad() {
this.getData();
},
onReady() { // 初步渲染完后执行
// 获取可视高度
uni.getSystemInfo({
success: (res) => {
// 可视区域高度 减去头部高度
this.clentHeight = res.windowHeight - this.getClientHeight();
}
})
},
methods: {
// 请求数据方法
getData(id){
if(id === this.activeIndex+1){
return;
}
$http.request({
url:'/goods/list'
}).then((res)=>{
let leftData = [];
let rightData = [];
res.forEach(v=>{
leftData.push({
id:v.id,
name:v.name
})
// 如果点击的id值相投
if(v.id=== (this.activeIndex+1)){
rightData.push(v.data);
}
})
this.leftData = leftData;
this.rightData = rightData;
console.log(rightData);
}).catch(()=>{
uni.showToast({
title:'请求失败',
icon:'none'
})
})
},
// 获取可视区域高度【兼容】
getClientHeight(){
const res = uni.getSystemInfoSync();
console.log(res.platform,res.statusBarHeight);
const system = res.platform;
if ( system === 'iso') {
return 44 + res.statusBarHeight;
}else if( system === 'android' ){
return 48 + res.statusBarHeight;
} else{
return 0;
}
},
// 左侧点击事件
changeLeftTab(index,id){
this.getData(id);
this.activeIndex = index;
},
},
// input 输入框dia点击事件
onNavigationBarSearchInputClicked() {
uni.navigateTo({
url:'/pages/search/search'
})
}
}
</script>
<style lang="scss">
.list{
display: flex;
}
.list-left{
width: 200rpx;
}
.left-item{
border-bottom:2rpx solid #ffffff;
font-size: 28rpx;
font-weight: bold;
background-color: #F7F7F7;
}
.left-name{
padding: 30rpx 6rpx;
text-align: center;
}
.left-name-active{
border-left:8rpx solid #49bdfb;
background-color: #ffffff;
}
.list-right{
flex: 1;
}
.list-title{
font-weight: bold;
padding: 30rpx 0;
}
.right-content{
display: flex;
flex-wrap: wrap;
}
.right-item{
width: 150rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 10rpx;
}
.right-name{
padding:16rpx 0;
}
.right-img{
width: 150rpx;
height: 150rpx;
}
</style>
代码 my.vue页面
我的页面,引入自定义组件Tabbar
<template>
<view class="my">
<!-- 头部 -->
<view class="my-header">
<view class="header-main">
<view class="header-config" @tap="goConfig">
<image class="config-img" src="../../static/tabbar/list.png" mode=""></image>
</view>
<view class="header-logo">
<image class="logo-img" :src="loginStatus ? userInfo.imgUrl :'../../static/tabbar/mySelected.png'" mode=""></image>
<view class="logo-name" @tap="goLogin">{{loginStatus ? userInfo.nickName : '用户昵称'}}</view>
</view>
</view>
</view>
<!-- 我的订单 -->
<view class="order">
<view class="order-title" @tap="goOrder">
<view class="">我的订单</view>
<view class="">全部订单 > </view>
</view>
<view class="order-list">
<view class="order-item">
<image class="order-img" src="../../static/logo.png" mode=""></image>
<view class="">待付款</view>
</view>
<view class="order-item">
<image class="order-img" src="../../static/logo.png" mode=""></image>
<view class="">待付款</view>
</view>
<view class="order-item">
<image class="order-img" src="../../static/logo.png" mode=""></image>
<view class="">待付款</view>
</view>
<view class="order-item">
<image class="order-img" src="../../static/logo.png" mode=""></image>
<view class="">待付款</view>
</view>
<view class="order-item">
<image class="order-img" src="../../static/logo.png" mode=""></image>
<view class="" @tap="qianzi">待付款1</view>
</view>
</view>
<!-- 内容列表 -->
<view class="my-content">
<view class="my-content-item" v-for="(item,index) in 6">
<view class="">
我的收藏
</view>
<view class="">
>
</view>
</view>
</view>
</view>
<Tabbar currentPage='my'></Tabbar>
</view>
</template>
<script>
import {mapState} from 'vuex'
import Tabbar from '@/components/common/Tabbar.vue';//引入
export default {
data() {
return {
}
},
components:{
Tabbar
},
computed:{
...mapState({
loginStatus:state=>state.user.loginStatus,
userInfo:state=>state.user.userInfo
})
},
methods: {
goConfig(){
uni.navigateTo({
url:'../my-config/my-config'
})
},
goOrder(){
uni.navigateTo({
url:'../my-order/my-order'
})
},
qianzi(){
uni.navigateTo({
url:'../qianzi/qianzi'
})
},
goLogin(){
uni.navigateTo({
url:'/pages/login/login'
})
}
}
}
</script>
<style lang="scss">
.my-header{
background-color: #eee;
width: 100%;
height: 400rpx;
}
.header-main{
position:relative;
top: 120rpx;
}
.header-config{
position: absolute;
left: 20rpx;
}
.header-logo{
position: absolute;
left:50%;
margin-left:-60rpx;
width: 120rpx;
}
.config-img{
width: 40rpx;
height: 40rpx;
}
.logo-img{
width: 120rpx;
height: 120rpx;
border:2rpx solid #ccc;
border-radius: 50%;
background-color: #FFFFFF;
}
.logo-name{
color: #FFFFFF;
font-size: 30rpx;
font-weight: bold;
text-align: center;
}
.order-title{
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx;
}
.order-list{
padding:20rpx;
display: flex;
}
.order-item{
flex:1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.order-img{
width: 80rpx;
height: 80rpx;
}
.my-content{
margin:20rpx 0;
padding: 0 20rpx;
}
.my-content-item{
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
border-bottom: 2px solid #ccc;
}
</style>
代码 shopcart.vue页面
购物车页面,引入自定义组件Tabbar
<template>
<view class="shop-cart">
<template v-if=" list.length > 0 ">
<!-- 自定义导航栏 -->
<uniNavBar
title="购物车"
:rightText=" isNavBar ? '完成' : '编辑'"
fixed="true"
statusBar="true"
@clickRight=" isNavBar = !isNavBar"
></uniNavBar>
<!-- 商品内容 -->
<view class="shop-item" v-for="(item,index) in list" :key="index">
<label for="" class="radio" @tap="selectedItem(index)">
<radio value="" color="#FF3333" :checked="item.checked" /> <text></text>
</label>
<image class="shop-img" :src="item.imgUrl" mode=""></image>
<view class="shop-text">
<view class="shop-name">
{{item.name}}
</view>
<view class="shop-color f-color">
{{item.color}}
</view>
<view class="shop-price">
<view class="">
¥{{item.pprice}}
</view>
<template v-if="!isNavBar">
<view>x {{item.num}}</view>
</template>
<template v-else>
<uniNumberBox
:value="item.num"
min="1"
@change="changeNumber($event,index)"
></uniNumberBox>
</template>
</view>
</view>
</view>
<!-- 底部 -->
<view class="shop-foot">
<label for="" class="radio foot-radio" @tap='checkedAllFn'>
<radio value="" color="#FF3333" :checked="checkedAll"></radio><text>全选</text>
</label>
<template v-if="!isNavBar">
<view class="foot-total">
<view class="foot-count">
合计:
<text class="f-active-color">
¥{{totalCount.pprice}}
</text>
</view>
<view class="foot-num" @tap="goConfirmOrder">
结算({{totalCount.num}})
</view>
</view>
</template>
<template v-else>
<view class="foot-total">
<view class="foot-num" style="background-color: black;">
移入收藏夹
</view>
<view class="foot-num" @tap="delGoodsFn">
删除
</view>
</view>
</template>
</view>
</template>
<template v-else>
<uniNavBar
title="购物车"
fixed="true"
statusBar="true"
></uniNavBar>
<view class="shop-else-list">
<text>囧~ 购物车还是空的~</text>
</view>
</template>
<Tabbar currentPage='shopcart'></Tabbar>
</view>
</template>
<script>
import uniNavBar from '@/components/uni/uni-nav-bar/uni-nav-bar.vue'
import uniNumberBox from '@/components/uni/uni-number-box/uni-number-box.vue'
import Tabbar from '@/components/common/Tabbar.vue';//引入
// 状态机引入
import {mapState,mapActions,mapGetters,mapMutations} from 'vuex'
export default {
data() {
return {
isNavBar:false,
}
},
computed:{
// 状态机数据处理
...mapState({
list:state=>state.cart.list
}),
...mapGetters(['checkedAll','totalCount'])
},
components:{
uniNavBar,uniNumberBox,Tabbar
},
methods: {
...mapActions(['checkedAllFn','delGoodsFn']),
...mapMutations(['selectedItem']),
changeNumber(value,index){
this.list[index].num = value;
},
// 进入确认订单
goConfirmOrder(){
uni.navigateTo({
url:'/pages/confirm-order/confirm-order'
})
}
}
}
</script>
<style lang="scss">
.shop-list{
padding-bottom: 100rpx;
}
.shop-else-list{
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: #f7f7f7;
display: flex;
align-items: center;
justify-content: center;
}
.shop-item{
display: flex;
padding: 20rpx;
align-items: center;
background-color: #f7f7f7;
margin-bottom: 10rpx;
}
.shop-img{
width: 200rpx;
height: 200rpx;
}
.shop-text{
flex: 1;
padding-left: 20rpx;
}
.shop-color{
font-size: 24rpx;
}
.shop-price{
display: flex;
justify-content: space-between;
}
.shop-foot{
border-top: 2rpx solid #f7f7f7;
background-color: #FFFFFF;
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 100rpx;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 100rpx;
}
.foot-radio{
padding-left: 20rpx;
}
.foot-total{
display: flex;
}
.foot-count{
line-height: 100rpx;
padding: 0 20rpx;
font-size: 32rpx;
}
.foot-num{
background-color: #49bdfb;
color: #FFFFFF;
padding: 0 60rpx;
line-height: 100rpx;
}
</style>
目录结构
前端目录结构
-
manifest.json 配置文件: appid、logo…
-
pages.json 配置文件: 导航、 tabbar、 路由
-
main.js vue初始化入口文件
-
App.vue 全局配置:样式、全局监视
-
static 静态资源:图片、字体图标
-
page 页面
- index
- index.vue
- list
- list.vue
- my
- my.vue
- my-config
- my-config.vue
- my-config
- my-config.vue
- my-add-path
- my-add-path.vue
- my-path-list
- my-path-list.vue
- search
- search.vue
- search-list
- search-list.vue
- shopcart
- shopcart.vue
- details
- details.vue
- my-order
- my-order.vue
- confirm-order
- confirm-order.vue
- payment
- payment.vue
- payment-success
- payment-success.vue
- login
- login.vue
- login-tel
login-tel.vue
- login-code
login-code.vue
- index
-
components 组件
- index
- Banner.vue
- Hot.vue
- Icons.vue
- indexSwiper.vue
- Recommend.vue
- Shop.vue
Tabbar.vue
- common
- Card.vue
- Commondity.vue
- CommondityList.vue
- Line.vue
- ShopList.vue
- order
- order-list.vue
- uni
- uni-number-box
- uni-number-box.vue
- uni-icons
- uni-icons.vue
- uni-nav-bar
- uni-nav-bar.vue
- mpvue-citypicker
- mpvueCityPicker.vue
- uni-number-box
- index
-
common 公共文件:全局css文件 || 全局js文件
- api
- request.js
- common.css
- uni.css
- api
-
store vuex状态机文件
- modules
- cart.js
- path.js
- user.js
- index.js
- modules