主页开发
保存主页数据
html
<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-dots | Boolean | false | 是否显示面板指示点 | |
indicator-color | Color | rgba(0, 0, 0, .3) | 指示点颜色 | |
indicator-active-color | Color | #000000 | 当前选中的指示点颜色 | |
active-class | String | swiper-item 可见时的 class | 支付宝小程序 | |
changing-class | String | acceleration 设置为 true 时且处于滑动过程中,中间若干屏处于可见时的class | 支付宝小程序 | |
autoplay | Boolean | false | 是否自动切换 | |
current | Number | 0 | 当前所在滑块的 index | |
current-item-id | String | 当前所在滑块的 item-id ,不能与 current 被同时指定 | 支付宝小程序不支持 | |
interval | Number | 5000 | 自动切换时间间隔 | |
duration | Number | 500 | 滑动动画时长 | app-nvue不支持 |
circular | Boolean | false | 是否采用衔接滑动,即播放到末尾后重新回到开头 | |
vertical | Boolean | false | 滑动方向是否为纵向 | |
previous-margin | String | 0px | 前边距,可用于露出前一项的一小部分,接受 px 和 rpx 值 | app-nvue、字节跳动小程序、飞书小程序不支持 |
next-margin | String | 0px | 后边距,可用于露出后一项的一小部分,接受 px 和 rpx 值 | app-nvue、字节跳动小程序、飞书小程序不支持 |
acceleration | Boolean | false | 当开启时,会根据滑动速度,连续滑动多屏 | 支付宝小程序 |
disable-programmatic-animation | Boolean | false | 是否禁用代码变动触发 swiper 切换时使用动画。 | 支付宝小程序 |
display-multiple-items | Number | 1 | 同时显示的滑块数量 | app-nvue、支付宝小程序不支持 |
skip-hidden-item-layout | Boolean | false | 是否跳过未显示的滑块布局,设为 true 可优化复杂情况下的滑动性能,但会丢失隐藏状态滑块的布局信息 | App、微信小程序、京东小程序 |
disable-touch | Boolean | false | 是否禁止用户 touch 操作 | App 2.5.5+、H5 2.5.5+、支付宝小程序、字节跳动小程序与飞书小程序(只在初始化时有效,不能动态变更) |
touchable | Boolean | true | 是否监听用户的触摸事件,只在初始化时有效,不能动态变更 | 字节跳动小程序与飞书小程序(uni-app 2.5.5+ 推荐统一使用 disable-touch) |
easing-function | String | default | 指定 swiper 切换缓动动画类型,有效值:default、linear、easeInCubic、easeOutCubic、easeInOutCubic | 微信小程序、快手小程序、京东小程序 |
@change | EventHandle | current 改变时会触发 change 事件,event.detail = {current: current, source: source} | ||
@transition | EventHandle | swiper-item 的位置发生改变时会触发 transition 事件,event.detail = {dx: dx, dy: dy},支付宝小程序暂不支持dx, dy | App、H5、微信小程序、支付宝小程序、字节跳动小程序、飞书小程序、QQ小程序、快手小程序 | |
@animationfinish | EventHandle | 动画结束时会触发 animationfinish 事件,event.detail = {current: current, source: source} | 字节跳动小程序与飞书小程序不支持 |
html
<!-- 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>
html
<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>
css
<style scoped lang="scss">
//提示:页面中可以使用标签选择器,但是组件中不能使用
.swiper {
height: 300rpx;
.swiper-item > image {
width: 750rpx;
}
}
</style>
功能菜单实现
需要安装uni-grid组件
Uni-Grid
组件名:uni-grid
宫格组件。
uni-grid 属性说明:
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
column | Number | 3 | 每列显示个数 |
borderColor | String | #d0dee5 | 边框颜色 |
showBorder | Boolean | true | 是否显示边框 |
square | Boolean | true | 是否方形显示 |
highlight | Boolean | true | 点击背景是否高亮 |
html
<!-- 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>
html
<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
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
title | String | - | 标题文字 |
sub-title | String | - | 副标题文字 |
extra | String | - | 标题额外信息 |
thumbnail | String | - | 标题左侧缩略图,支持网络图片,本地图片,本图片需要传入一个绝对路径,如:/static/xxx.png |
cover | String | - | 封面图,支持网络图片,本地图片,本图片需要传入一个绝对路径,如:/static/xxx.png |
is-full | Boolean | false | 卡片内容是否通栏,为true时将去除padding值 |
is-shadow | Boolean | false | 卡片内容是否开启阴影 |
shadow | String | 0px 0px 3px 1px rgba(0, 0, 0, 0.08) | 卡片阴影,需符合 css 值 |
border | Boolean | true | 卡片边框 |
margin | String | 10px | 卡片外边距 |
spacing | String | 10px | 卡片内边距 |
padding | String | 10px | 卡片内容内边距 |
border | Boolean | true | 卡片边框 |
mode[弃用] | String | basic | 卡片模式 ,可选值, basic:基础卡片 ;style :图文卡片 ; title :标题卡片 |
note[弃用] | String | - | 底部信息 |
html
<!-- 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
css
//清除自定义样式对系统默认组件的影响
view.card {
flex-direction: column;
//使用!important提升当前样式的优先级,可以覆盖系统默认样式
margin-top: $uni-spacing-col-base !important;
}
css
.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
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
border | Boolean | true | 是否显示边框 |
ListItem Props
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
title | String | - | 标题 |
note | String | - | 描述 |
ellipsis | Number | 0 | title 是否溢出隐藏,可选值,0:默认; 1:显示一行; 2:显示两行; |
thumb | String | - | 左侧缩略图,若thumb有值,则不会显示扩展图标 |
thumbSize | String | medium | 略缩图尺寸,可选值,lg:大图; medium:一般; sm:小图; |
showBadge | Boolean | false | 是否显示数字角标 |
badgeText | String | - | 数字角标内容 |
badgeType | String | - | 数字角标类型,参考uni-icons |
badgeStyle | Object | - | 数字角标样式,使用uni-badge的custom-style参数 |
rightText | String | - | 右侧文字内容 |
disabled | Boolean | false | 是否禁用 |
showArrow | Boolean | true | 是否显示箭头图标 |
link | String | navigateTo | 新页面跳转方式,可选值见下表 |
to | String | - | 新页面跳转地址,如填写此属性,click 会返回页面是否跳转成功 |
clickable | Boolean | false | 是否开启点击反馈 |
showSwitch | Boolean | false | 是否显示Switch |
switchChecked | Boolean | false | Switch是否被选中 |
showExtraIcon | Boolean | false | 左侧是否显示扩展图标 |
extraIcon | Object | - | 扩展图标参数,格式为 {color: '#4cd964',size: '22',type: 'spinner'} ,参考 uni-icons |
direction | String | row | 排版方向,可选值,row:水平排列; column:垂直排列; 3个插槽是水平排还是垂直排,也受此属性控制 |
ListItem Slots
名称 | 说明 |
---|---|
header | 左/上内容插槽,可完全自定义默认显示 |
body | 中间内容插槽,可完全自定义中间内容 |
footer | 右/下内容插槽,可完全自定义右侧内容 |
通过插槽扩展 需要注意的是当使用插槽时,内置样式将会失效,只保留排版样式,此时的样式需要开发者自己实现 如果
uni-list-item
组件内置属性样式无法满足需求,可以使用插槽来自定义uni-list-item里的内容。 uni-list-item提供了3个可扩展的插槽:header
、body
、footer
- 当
direction
属性为row
时表示水平排列,此时header
表示列表的左边部分,body
表示列表的中间部分,footer
表示列表的右边部分- 当
direction
属性为column
时表示垂直排列,此时header
表示列表的上边部分,body
表示列表的中间部分,footer
表示列表的下边部分 开发者可以只用1个插槽,也可以3个一起使用。在插槽中可自主编写view标签,实现自己所需的效果。
html
<!-- 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;
css
/* 自定义的样式混入 */
@mixin ellipsis-1 {
white-space: nowrap; //文本在空白处不换行
overflow: hidden; //内容溢出的话则隐藏
text-overflow: ellipsis; //如果发生文本溢出则用省略号代替
}
@mixin ellipsis-2 {
}
@mixin ellipsis-3 {
}
css
.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
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
标题栏开发
html
<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>
html
<script>
export default {
data() {
return {
}
},
methods: {
//处理"导航条左侧按钮"被单击事件
back(){
uni.navigateBack()
},
//处理"导航条右侧按钮"被单击事件
jump(url){
uni.navigateTo({ url })
},
}
}
</script>
css
<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
html
<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
html
<template>
<view class="c1">
CHILD1
<view class="c2">
CHILD2
</view>
</view>
</template>
<style scoped lang="scss">
</style>
累积费用统计开发
html
<!-- F2:累积费用统计 -->
<view class="fee-statistics">
<text class="hint">当前待缴费用(元)</text>
<text class="amount">¥{{amount}}</text>
</view>
css
.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;
}
}
各项费用的列表开发
html
<!-- 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>
html
<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>
js
/**
* 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
}