Bootstrap

Uni-App-04

主页开发

保存主页数据

<script>
import { indexData, base } from '@/service'
	export default {
		data() {
			return {
				base,				//把服务器基础地址变量设置为数据属性
				carousels:[],		//轮播广告条目列表
				menuItems:[],		//当前用户选中的功能菜单列表
				activities:[],		//最新的社区活动列表
			}
		},
		//生命周期方法:组件加载完成
		async onLoad() {	
			//弹出“欢迎回来”提示框
			uni.showToast({
				title:'欢迎回来',
				icon: 'none',
				duration: 3000
			})
			//向服务器请求首页数据
			let data = await indexData()
			this.carousels = data.carousels		//轮播广告
			this.menuItems = data.menuItems		//功能菜单
			this.activities = data.activities	//社区活动
		},
		methods: {
			
		}
	}
</script>

轮播图实现

swiper

滑块视图容器。

一般用于左右滑动或上下滑动,比如banner轮播图。

注意滑动切换和滚动的区别,滑动切换是一屏一屏的切换。swiper下的每个swiper-item是一个滑动切换区域,不能停留在2个滑动区域之间。

属性说明

属性名类型默认值说明平台差异说明
indicator-dotsBooleanfalse是否显示面板指示点
indicator-colorColorrgba(0, 0, 0, .3)指示点颜色
indicator-active-colorColor#000000当前选中的指示点颜色
active-classStringswiper-item 可见时的 class支付宝小程序
changing-classStringacceleration 设置为 true 时且处于滑动过程中,中间若干屏处于可见时的class支付宝小程序
autoplayBooleanfalse是否自动切换
currentNumber0当前所在滑块的 index
current-item-idString当前所在滑块的 item-id ,不能与 current 被同时指定支付宝小程序不支持
intervalNumber5000自动切换时间间隔
durationNumber500滑动动画时长app-nvue不支持
circularBooleanfalse是否采用衔接滑动,即播放到末尾后重新回到开头
verticalBooleanfalse滑动方向是否为纵向
previous-marginString0px前边距,可用于露出前一项的一小部分,接受 px 和 rpx 值app-nvue、字节跳动小程序、飞书小程序不支持
next-marginString0px后边距,可用于露出后一项的一小部分,接受 px 和 rpx 值app-nvue、字节跳动小程序、飞书小程序不支持
accelerationBooleanfalse当开启时,会根据滑动速度,连续滑动多屏支付宝小程序
disable-programmatic-animationBooleanfalse是否禁用代码变动触发 swiper 切换时使用动画。支付宝小程序
display-multiple-itemsNumber1同时显示的滑块数量app-nvue、支付宝小程序不支持
skip-hidden-item-layoutBooleanfalse是否跳过未显示的滑块布局,设为 true 可优化复杂情况下的滑动性能,但会丢失隐藏状态滑块的布局信息App、微信小程序、京东小程序
disable-touchBooleanfalse是否禁止用户 touch 操作App 2.5.5+、H5 2.5.5+、支付宝小程序、字节跳动小程序与飞书小程序(只在初始化时有效,不能动态变更)
touchableBooleantrue是否监听用户的触摸事件,只在初始化时有效,不能动态变更字节跳动小程序与飞书小程序(uni-app 2.5.5+ 推荐统一使用 disable-touch)
easing-functionStringdefault指定 swiper 切换缓动动画类型,有效值:default、linear、easeInCubic、easeOutCubic、easeInOutCubic微信小程序、快手小程序、京东小程序
@changeEventHandlecurrent 改变时会触发 change 事件,event.detail = {current: current, source: source}
@transitionEventHandleswiper-item 的位置发生改变时会触发 transition 事件,event.detail = {dx: dx, dy: dy},支付宝小程序暂不支持dx, dyApp、H5、微信小程序、支付宝小程序、字节跳动小程序、飞书小程序、QQ小程序、快手小程序
@animationfinishEventHandle动画结束时会触发 animationfinish 事件,event.detail = {current: current, source: source}字节跳动小程序与飞书小程序不支持
<!-- F1: 轮播广告 -->
<!-- indicator-dots:是否显示“小圆饼”指示器 -->
<!-- autoplay:是否自动播放轮播广告 -->
<!-- interval:时间间隔,两个广告间停留时间 -->
<!-- duration:持续时长,一个广告的过渡动画持续时长 -->
<swiper class="swiper" :indicator-dots="true" :autoplay="true" :interval="2000" :duration="500">
    <swiper-item v-for="(c, i) in carousels" :key="i">
        <view class="swiper-item">
             <image @click="jump(c.href)" :src="base + c.pic" mode="widthFix"/>
        </view>
    </swiper-item>	
</swiper>
<script>
import { indexData, base } from '@/service'
	export default {
		data() {
			return {
				base,				//把服务器基础地址变量设置为数据属性
				carousels:[],		//轮播广告条目列表
				menuItems:[],		//当前用户选中的功能菜单列表
				activities:[],		//最新的社区活动列表
			}
		},
		//生命周期方法:组件加载完成
		async onLoad() {	
			//弹出“欢迎回来”提示框
			uni.showToast({
				title:'欢迎回来',
				icon: 'none',
				duration: 3000
			})
			//向服务器请求首页数据
			let data = await indexData()
			console.log(data);
			this.carousels = data.carousels		//轮播广告
			this.menuItems = data.menuItems		//功能菜单
			this.activities = data.activities	//社区活动
		},
		methods: {
			jump(url){
				console.log(url)
				//导航跳转到指定页
				uni.navigateTo({ url })
			}
		}
	}
</script>
<style scoped lang="scss">
	//提示:页面中可以使用标签选择器,但是组件中不能使用		
	.swiper {
		height: 300rpx;
		.swiper-item > image {
			width: 750rpx;
		}
	}
</style>

功能菜单实现

需要安装uni-grid组件

Uni-Grid

组件名:uni-grid

宫格组件。

uni-grid 属性说明:

属性名类型默认值说明
columnNumber3每列显示个数
borderColorString#d0dee5边框颜色
showBorderBooleantrue是否显示边框
squareBooleantrue是否方形显示
highlightBooleantrue点击背景是否高亮
<!-- F2: 功能菜单 -->
<!-- column:一行中默认显示几列 -->
<!-- showBorder:是否显示边框 -->
<!-- square:每个宫格项是否显示为方形 -->
<uni-grid class="func-menu" :column="4" :showBorder="false" :square="true">
    <uni-grid-item v-for="(item,i) in menuItems" :key="i">
        <view class="menu-item" @click="jump(item.href)">
            <image :src="base+item.pic" mode="widthFix"/>
            <text>{{item.title}}</text>
        </view>
    </uni-grid-item>
</uni-grid>
<style scoped lang="scss">
    .func-menu {
		margin-top: $uni-spacing-col-base;
		background-color: $uni-bg-color;
		.menu-item {
			height: 100%;
			//把弹性容器的主轴方向修改为:纵向
			flex-direction: column;
			//弹性容器中的子元素在主轴方向上居中对齐
                justify-content: center;
			//弹性容器中的子元素交叉轴方向上居中对齐
			align-items: center;
			> image { width:35%; margin-bottom: $uni-spacing-col-sm; }
		}
	}
</style>

商业服务功实现

需要安装uni-card扩展组件

组件名:uni-card

Uni-Card

卡片组件通常用来显示完整独立的一段信息,同时让用户理解他的作用。例如一篇文章的预览图、作者信息、时间等,卡片通常是更复杂和更详细信息的入口点。

Card Props
属性名类型默认值说明
titleString-标题文字
sub-titleString-副标题文字
extraString-标题额外信息
thumbnailString-标题左侧缩略图,支持网络图片,本地图片,本图片需要传入一个绝对路径,如:/static/xxx.png
coverString-封面图,支持网络图片,本地图片,本图片需要传入一个绝对路径,如:/static/xxx.png
is-fullBooleanfalse卡片内容是否通栏,为true时将去除padding值
is-shadowBooleanfalse卡片内容是否开启阴影
shadowString0px 0px 3px 1px rgba(0, 0, 0, 0.08)卡片阴影,需符合 css 值
borderBooleantrue卡片边框
marginString10px卡片外边距
spacingString10px卡片内边距
paddingString10px卡片内容内边距
borderBooleantrue卡片边框
mode[弃用]Stringbasic卡片模式 ,可选值, basic:基础卡片 ;style :图文卡片 ; title :标题卡片
note[弃用]String-底部信息
<!-- F3: 商业服务 -->
<!-- isFull:是否显示为“通栏卡片”(左右撑满) -->
<uni-card class="card" title="| 商业服务" is-full>
	<view class="service">
		<!-- 左侧:房屋租售 -->
		<view class="service-item">
			<view class="txt">
				<text>房屋租售</text>
				<view>
					<navigator>租房</navigator>
					<navigator>短租</navigator>
				</view>
			</view>	
			<!-- 图片缩放模式1:widthFix -->
			<!-- 图片缩放模式2:scaleToFill:不保持原始宽高比,缩放图片填满指定宽高 -->
			<image class="img" mode="scaleToFill" src="../../static/img/chuzu.png"/>
		</view>
		<!-- 右侧:便民服务 -->
		<view class="service-item">
			<view class="txt">
				<text>便民服务</text>
				<view>
					<navigator>便利店</navigator>
					<navigator>超市</navigator>
				</view>
			</view>	
			<image class="img" mode="scaleToFill" src="../../static/img/bianmin.png"/>
		</view>
	</view>
</uni-card>

App.vue

//清除自定义样式对系统默认组件的影响
view.card {
    flex-direction: column;
    //使用!important提升当前样式的优先级,可以覆盖系统默认样式
    margin-top: $uni-spacing-col-base !important;
}
.service {
	width: 100%;
	.service-item {
		//弹性子元素尺寸增长权重为:1
		flex: 1;
		padding-top: $uni-spacing-col-sm;
		padding-bottom: $uni-spacing-col-sm;
		&:nth-child(1) {padding-right: $uni-spacing-row-sm;}
		&:nth-child(2) {padding-left: $uni-spacing-row-sm;}
		justify-content: space-between; //弹性容器中的子元素在主轴方向上:空白在中央
		align-items: center; //弹性容器中的子元素在交叉轴方向上:居中对齐
		.img { width: 150rpx; height: 120rpx;}
		.txt { 
			font-size: $uni-font-size-sm; 
			flex-direction: column; 
			flex: 1; //弹性子元素尺寸增长权重:1
			navigator {margin-right: $uni-spacing-row-sm;}
		}
	}
}

社区活动功能实现

需要安装uni-ist

Uni-List

组件名:uni-list

List 列表组件,包含基本列表样式、可扩展插槽机制、长列表性能优化、多端兼容。

List Props
属性名类型默认值说明
borderBooleantrue是否显示边框
ListItem Props
属性名类型默认值说明
titleString-标题
noteString-描述
ellipsisNumber0title 是否溢出隐藏,可选值,0:默认; 1:显示一行; 2:显示两行;
thumbString-左侧缩略图,若thumb有值,则不会显示扩展图标
thumbSizeStringmedium略缩图尺寸,可选值,lg:大图; medium:一般; sm:小图;
showBadgeBooleanfalse是否显示数字角标
badgeTextString-数字角标内容
badgeTypeString-数字角标类型,参考uni-icons
badgeStyleObject-数字角标样式,使用uni-badge的custom-style参数
rightTextString-右侧文字内容
disabledBooleanfalse是否禁用
showArrowBooleantrue是否显示箭头图标
linkStringnavigateTo新页面跳转方式,可选值见下表
toString-新页面跳转地址,如填写此属性,click 会返回页面是否跳转成功
clickableBooleanfalse是否开启点击反馈
showSwitchBooleanfalse是否显示Switch
switchCheckedBooleanfalseSwitch是否被选中
showExtraIconBooleanfalse左侧是否显示扩展图标
extraIconObject-扩展图标参数,格式为 {color: '#4cd964',size: '22',type: 'spinner'},参考 uni-icons
directionStringrow排版方向,可选值,row:水平排列; column:垂直排列; 3个插槽是水平排还是垂直排,也受此属性控制
ListItem Slots
名称说明
header左/上内容插槽,可完全自定义默认显示
body中间内容插槽,可完全自定义中间内容
footer右/下内容插槽,可完全自定义右侧内容

通过插槽扩展 需要注意的是当使用插槽时,内置样式将会失效,只保留排版样式,此时的样式需要开发者自己实现 如果 uni-list-item 组件内置属性样式无法满足需求,可以使用插槽来自定义uni-list-item里的内容。 uni-list-item提供了3个可扩展的插槽:headerbodyfooter

  • direction 属性为 row 时表示水平排列,此时 header 表示列表的左边部分,body 表示列表的中间部分,footer 表示列表的右边部分
  • direction 属性为 column 时表示垂直排列,此时 header 表示列表的上边部分,body 表示列表的中间部分,footer 表示列表的下边部分 开发者可以只用1个插槽,也可以3个一起使用。在插槽中可自主编写view标签,实现自己所需的效果。
<!-- F4: 社区活动 -->
<uni-card class="card" title="| 社区活动" is-full padding="0">
    <!-- clickable:是否允许列表项被点击,如果允许则自动添加点击反馈 -->
    <!-- link:是否允许列表项显示为链接,如果允许则自动添加点击反馈 + 添加向右的箭头 -->
    <uni-list>
                <uni-list-item class="activity-item" v-for="(a,i) in activities" :key="i">
            <!-- 列表项中央主体插槽内容 -->
            <template v-slot:body>
                <view class="activity-body">
                    <text class="title">{{a.title}}</text>
                    <text class="content">{{a.content}}</text>
                    <!-- 通过管道(|)把数据传递给过滤器 -->
                    <text class="time">{{a.startTime | datetime}}</text>
                </view>
                </template>
            <!-- 列表项右侧插槽内容 -->
            <template v-slot:footer>
                <image class="pic" :src="base+a.pic" mode="widthFix"/>
            </template>
        </uni-list-item>
    </uni-list>
</uni-card>

uni.scss

CSS中如何实现“文本在一行中显示,如果有溢出,则使用省略号代替”?

white-space: nowrap;

overflow: hidden;

text-overflow: ellipsis;

/* 自定义的样式混入 */
@mixin ellipsis-1 {
	white-space: nowrap;		//文本在空白处不换行
	overflow: hidden;			//内容溢出的话则隐藏
	text-overflow: ellipsis;	//如果发生文本溢出则用省略号代替
}
@mixin ellipsis-2 {
	
}
@mixin ellipsis-3 {
	
}
.activity-item {
    align-items: center;//弹性容器中的子元素交叉轴上居中对齐
    .activity-body {
        flex-direction: column;
        width: 420rpx; //父元素指定宽度,子元素就可能显示省略号
        .title {
            font-size: $uni-font-size-base - 1;
            @include ellipsis-1;
        }
        .content {
            font-size: $uni-font-size-sm; 
            color:$uni-text-color-grey;
            @include ellipsis-1;
        }
        .time {
            font-size: $uni-font-size-sm; 
            color:$uni-text-color-grey;
		}
	}
	.pic {
        width:240rpx;
        flex-shrink: 0;//弹性子元素尺寸收缩权重: 0 —— 参与尺寸缩小
    }
}

过滤器

在这里插入图片描述

main.js

import App from './App'

// #ifndef VUE3
import Vue from 'vue'
Vue.config.productionTip = false
//声明全局过滤器——可供所有的页面/组件使用
Vue.filter('datetime', (num)=>{
	//参数是数字——时间戳,返回日期时间字符串
	let d = new Date(num)  //把时间戳转换为日期对象
	//获取Date中的不同部分
	let yy = d.getFullYear()
	let mm = d.getMonth() + 1   //注意:默认的月份是0~11
	mm = mm<10 ? '0'+mm : mm
	let dd = d.getDate()
	dd = dd<10 ? '0'+dd : dd
	let hh = d.getHours()
	hh = hh<10 ? '0'+hh : hh
	let mi = d.getMinutes()
	mi = mi<10 ? '0'+mi : mi
	return `${yy}-${mm}-${dd} ${hh}:${mi}`
})

App.mpType = 'app'
const app = new Vue({
    ...App
})
app.$mount()
// #endif

缴费列表页面开发

property-fee.vue

标题栏开发

<template>
	<view class="content">
		<!-- F1:自定义标题栏 -->
		<uni-nav-bar class="nav-bar" left-icon="back" left-text="返回" 
			right-icon="list" title="缴 费" background-color="#090" color="#fff" 
			@clickLeft="back" @clickRight="jump('/pages/feeRecord/feeRecord')">
		</uni-nav-bar>
		<!-- F2:累积费用统计 -->
		<!-- F3:分列各项费用的列表 -->
	</view>
</template>
<script>
	export default {
		data() {
			return {
				
			}
		},
		methods: {
			//处理“导航条左侧按钮”被单击事件
			back(){
				uni.navigateBack()
			},
			//处理“导航条右侧按钮”被单击事件
			jump(url){
				uni.navigateTo({ url })
			},
		}
	}
</script>
<style scoped lang="scss">
.nav-bar {
	display: block;
	//让父组件中的样式可以穿透到子组件
	::v-deep  .uni-navbar__content {	
		display: block;
	}
}
</style>

样式穿透

Vue.js / uni-app经典面试题:样式穿透是什么?

设法让父组件因为使用scoped属性产生的data-v-随机数 属性在所有的子组件体内元素上也生成一份,这样一来父组件的样式就可以应用到子组件内 —— 样式穿透:让父组件内编写的样式可以作用到子组件内。实现方法:

CSS: >>> 选择器 { }

Less: /deep/ 选择器 { }

SCSS: ::v-deep 选择器 { }

页面 parent.vue

<template>
	<view class="p1">
		PARENT1
		<view class="p2">PARENT2</view>
		<!-- 使用子组件 -->
		<zh-child/>
	</view>
</template>

<style scoped lang="scss">
.p1 {
	font-size: 1.5em;
	font-weight: bold;
}
.p2 {
	font-style: italic;
}
.c1 {
	color: red;
}
>>>  .c2 {
	text-decoration: line-through;
}
</style>

组件 zh-child.vue

<template>
	<view class="c1">
		CHILD1
		<view class="c2">
			CHILD2
		</view>
	</view>
</template>

<style scoped lang="scss">

</style>

累积费用统计开发

<!-- F2:累积费用统计 -->
<view class="fee-statistics">
    <text class="hint">当前待缴费用(元)</text>
    <text class="amount">¥{{amount}}</text>
</view>
.fee-statistics {
	background-color: $zh-theme-color;
	border-top: $uni-border;
	padding: $uni-spacing-col-lg*3  0;
	flex-direction: column; //弹性容器的主轴方向:纵向
	align-items: center; //弹性容器中的子元素在交叉轴上居中对齐
	color: $uni-text-color-inverse;
	.hint {
		font-size: $uni-font-size-lg; 
		margin-bottom: $uni-spacing-col-base;
	}
	.amount { 
		font-size: $uni-font-size-base * 2.5;
		font-weight: bold;
	}
}

各项费用的列表开发

<!-- F3:分列各项费用的列表 -->
<!-- thumb:有时也称为thumbnail,拇指图/缩略图 -->
<!-- thumbSize:缩略图大小,只能选择 sm/base/lg 之一 -->
<!-- link:列表选项显示为链接效果:点击后有反馈+可以点击+右侧箭头 -->
<!-- rightText:显示在右侧的文字 -->
<!-- Math.abs():返回数字的绝对值 -->
<uni-list>
    <uni-list-item title="水费" thumb="../../static/img/shui.png" thumbSize="lg" link 
    	:rightText="fees.shui>=0 ? '' : `待缴金额:¥${Math.abs(fees.shui)}`" to="/pages/feePay/feePay?type=1"/>
    <uni-list-item title="电费" thumb="../../static/img/dian.png" thumbSize="lg" link 
    	:rightText="fees.dian>=0 ? '' : `待缴金额:¥${Math.abs(fees.dian)}`" to="/pages/feePay/feePay?type=2"/>
    <uni-list-item title="燃气费" thumb="../../static/img/ranqi.png" thumbSize="lg" link 
         :rightText="fees.ranqi>=0 ? '' : `待缴金额:¥${Math.abs(fees.ranqi)}`" to="/pages/feePay/feePay?type=3"/>
    <uni-list-item title="物业费" thumb="../../static/img/wuye.png" thumbSize="lg" link 
         :rightText="fees.wuye>=0 ? '' : `待缴金额:¥${Math.abs(fees.wuye)}`" to="/pages/feePay/feePay?type=4"/>
    <uni-list-item title="停车费" thumb="../../static/img/tingche.png" thumbSize="lg" link 
    	:rightText="fees.tingche>=0 ? '' : `待缴金额:¥${Math.abs(fees.tingche)}`" to="/pages/feePay/feePay?type=5"/>
    <uni-list-item title="宽带费" thumb="../../static/img/kuandai.png" thumbSize="lg" link 
    	:rightText="fees.kuandai>=0 ? '' : `待缴金额:¥${Math.abs(fees.kuandai)}`" to="/pages/feePay/feePay?type=6"/>
</uni-list>
<script>
	import { feeList } from '@/service'
	export default {
		data() {
			return {
				fees: {},	//待缴费用
			}
		},
		//生命周期方法 —— 页面挂载完成时
		async onLoad(){
			//异步请求当前登录用户的待缴费用
			let data = await feeList()
			this.fees = data
		},
		//计算属性
		computed: {
			amount(){
				let sum = 0;
				sum = this.fees.shui<0 ? sum+this.fees.shui : sum
				sum = this.fees.dian<0 ? sum+this.fees.dian : sum
				sum = this.fees.ranqi<0 ? sum+this.fees.ranqi : sum
				sum = this.fees.tingche<0 ? sum+this.fees.tingche : sum
				sum = this.fees.wuye<0 ? sum+this.fees.wuye : sum
				sum = this.fees.kuandai<0 ? sum+this.fees.kuandai : sum
				//先求数字的绝对值,再保留指定长度的小数位
				return Math.abs(sum).toFixed(2)
			}
		},
		methods: {
			//处理“导航条左侧按钮”被单击事件
			back(){
				uni.navigateBack()
			},
			//处理“导航条右侧按钮”被单击事件
			jump(url){
				uni.navigateTo({ url })
			},
		}
	}
</script>
/**
 * API-3.1、当前登录用户各项待缴费金额汇总
 * 接口地址:fee/list
 * 请求方式:GET
 * 请求头部:token - 用户登录后保存在客户端的身份凭证
 */
export let feeList = async ( )=>{
	//1.准备请求URL
	let url = base + 'fee/list'
	//2.显示“加载中”提示框	
	uni.showLoading({
		title: '缴费数据读取中'
	})
	//3.发起异步请求消息
	let [err, res] = await uni.request({
		url,		//请求地址
		header: {	//请求头部-token(客户端身份令牌)
			token: uni.getStorageSync('userToken')
		}
	})
	//4.隐藏“加载中”提示框
	uni.hideLoading()
	//5.返回响应消息主体
	return res.data
}
;