任务三 工单4
本工单最终效果图:
1、在“首页展示”界面,用户可以参与许多优惠活动,如“买茶送包”、“会员券包”、“优质茶礼盒”等活动。通过活动可获得相关奖励、分享好友、打折券等好处。点击如图3.4.4区域,进入“买茶送包”界面,在该界面可点亮进度条,获得买茶送包券。
新建“买茶送包”界面并命名invite.vue,完成pages.json中样式设置并设置@tap="invite"跳转,代码如下:
{
"path" : "pages/invite/invite",
"style" :
{
"navigationBarTitleText": "买茶送包",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff"
}
}
按照UI设计师标准完成invite.vue的界面(图3.4.1)搭建,通过设置界面background属性为一张url图片保证界面美观。其次,分为两个view(样式为invite-box、my-award)一个image完成剩下布局。布局样式如图3.4.5。
“买茶送包”界面代码invite.vue
<template>
<!-- 买茶送包 -->
<view class="container">
<view class="invite-box">
<view class="w-100 d-flex justify-content-end align-items-baseline font-size-sm">
<view class="text-color-danger font-size-lg">0</view>
<view class="text-color-assist">/3</view>
</view>
<view class="progress-box">
<progress :percent="1/3 * 100" backgroundColor="#FEEBD5" activeColor="#FEDC5F" stroke-width="30"
border-radius="30"></progress>
</view>
<view class="d-flex flex-column text-color-danger font-size-sm align-items-center mb-40">
<view>每邀3位新用户点亮,得买茶送包劵1张</view>
<view>新用户可得30元权益劵包</view>
</view>
<view class="d-flex just-content-center" style="opacity: 0.8;" hover-class="opacity-1">
<view class="w-90 position-relative">
<image src="https://s3.uuu.ovh/imgs/2024/12/12/1cc837e93d6a074f.png" class="w-100"
mode="widthFix"></image>
</view>
<view class="font-size-lg text-color-danger position-absolute d-flex align-items-center mt-30">
<view>立即找好友助力</view>
<image class="to-invite-img"
src="https://s3.uuu.ovh/imgs/2024/12/12/f5541573e4c26619.png" mode="widthFix">
</image>
</view>
</view>
</view>
<view class="my-award">
<image src="https://s3.uuu.ovh/imgs/2024/12/12/bfe3e7ba232f2c57.png" class="my-award-img"
mode="widthFix"></image>
<view class="item">
<view class="font-size-lg text-color-danger">0</view>
<view class="font-size-sm text-color-assist">已成功邀请(位)</view>
</view>
<view class="item">
<view class="font-size-lg text-color-danger">0</view>
<view class="font-size-sm text-color-assist">已获得奖励(次)</view>
</view>
</view>
<image src='https://s3.uuu.ovh/imgs/2024/12/12/d90cb073a37869a4.png' class="w-100 firend-award"
mode="widthFix"></image>
</view>
</template>
<script>
export default {
data() {
return {
};
}
}
</script>
<style lang="scss" scoped>
page {
background: url('https://s3.uuu.ovh/imgs/2024/12/12/c57f2423e1f976f9.png') no-repeat;
background-size: 100% auto;
padding: 576rpx 30rpx 0;
height: auto;
}
.invite-box {
padding: 40rpx 70rpx;
background-color: #FFFFFF;
border-radius: 8rpx;
display: flex;
flex-direction: column;
position: relative;
margin-bottom: 30rpx;
}
.progress-box {
width: 100%;
margin: 20rpx 0;
}
.to-invite-img {
width: 16rpx;
margin-left: 10rpx;
}
.opacity-1 {
opacity: 1 !important;
}
.my-award {
padding: 60rpx 0;
display: flex;
justify-content: center;
align-items: center;
background-color: #FFFFFF;
border-radius: 8rpx;
margin-bottom: 30rpx;
position: relative;
.my-award-img {
width: 291rpx;
position: absolute;
top: -15rpx;
}
.item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
&::after {
position: absolute;
content: ' ';
background-color: #ccc;
width: 2rpx;
height: 100%;
right: 0;
top: 0;
transform: scaleY(0.5);
}
&:nth-last-child(1) {
&::after {
width: 0;
}
}
}
}
</style>
★2、点击会员券包区域,通过index.vue界面通过@tap="packages"方法跳转至“券包商品”界面。新建packages.vue页面,该页面为“券包商品”界面,其中该页面含有下拉刷新功能,可以通过pages.json中的"enablePullDownRefresh": true进行设置,代码如下:
{
"path" : "pages/packages/packages",
"style" :
{
"navigationBarTitleText": "券包商品",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff",
"enablePullDownRefresh": true
}
}
在packages.vue界面中通过以下方法完成对下拉刷新后的回调操作,完成刷新后利用uni.stopPullDownRefresh()方法停止下拉刷新效果,效果如图3.4.6所示。
async onPullDownRefresh() {
this.packages = []//下拉刷新重置数据
await this.getPackages()//获取新数据
// 当刷新操作完成后,停止下拉刷新效果
uni.stopPullDownRefresh();
},
页面中间优惠券卡片列表使用v-for循环完成布局,调用this.$api('packages')获取优惠券数据,通过packages数组完成数据绑定。效果如图3.4.7,代码如下:
<view class="d-flex align-items-center bg-white"
style="padding: 30rpx; height: 220rpx; margin-bottom: 34rpx; border-radius: 8rpx;"
v-for="(item, index) in packages" :key="index">
<image :src="item.image" style="width: 200rpx; height: 160rpx; margin-right: 20rpx;"></image>
<view class="d-flex flex-fill flex-column justify-content-between" style="height: 160rpx;">
<view class="font-size-lg text-color-base">{{ item.title }}</view>
<view class="d-flex justify-content-between align-items-center">
<view class="font-size-sm">¥{{ item.amount }}</view>
<button type="primary" size="mini" plain class="pay-btn">去购买</button>
</view>
</view>
</view>
由于后端工程师并为将真是api开发出来,所以该页面模拟网络请求,通过引入自定义loading组件(图3.4.8)模拟请求时加载状态。使用setTimeout()函数延迟1秒钟获取数据,loading组件为components文件夹下新建vue文件,中间为一张image图片,通过命名name: "Loading"将组件维护起来后续复用,效果如图3.4.9,可见代码如下:
<template>
<view class="loading">
<image src="/static/images/loading.gif"></image>
</view>
</template>
<script>
export default {
name: 'Loading'
}
</script>
<style lang="scss">
.loading {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
image {
width: 260rpx;
height: 260rpx;
position: relative;
margin-top: 200rpx;
}
}
</style>
在data中设置loading布尔参数,并通过v-if、v-else完成loading组件的显示与消失,代码如下,最终效果图如图3.4.10。
<!-- 券包商品 -->
<view class="container w-100 h-100" v-if="!loading">
...
...
<loading v-else></loading>
index.vue跳转代码
...
...
<!-- ringt 买茶送包 会员劵包 -->
<view class="right">
<view class="tea-activity" @tap="invite">
<image src="/static/images/index/mcsb.png" class="mark-img"></image>
<view>买茶送包</view>
<view class="right-img">
<image src="/static/images/index/mcsb_bg.png" mode="widthFix"></image>
</view>
</view>
<view class="member-gifts" @tap="packages">
<image src="/static/images/index/hyjb.png" class="mark-img"></image>
<view>会员劵包</view>
<view class="right-img">
<image src="/static/images/index/hyjb_bg.png" mode="widthFix"></image>
</view>
</view>
</view>
</view>
...
...
<script>
import {mapGetters,mapState} from 'vuex'
export default {
...
},
methods: {
...
...
...
invite() {//买茶送包
uni.navigateTo({
url: '/pages/invite/invite'
})
},
packages() {//跳转券包商品
uni.navigateTo({
url: '/pages/packages/packages'
})
},
}
}
</script>
“券包商品”界面代码packages.vue
<template>
<!-- 券包商品 -->
<view class="container w-100 h-100" v-if="!loading">
<!-- 卡片 -->
<view class="d-flex flex-column w-100" style="padding: 30rpx; padding-bottom: -34rpx; margin-bottom: 150rpx;">
<view class="d-flex align-items-center bg-white"
style="padding: 30rpx; height: 220rpx; margin-bottom: 34rpx; border-radius: 8rpx;"
v-for="(item, index) in packages" :key="index">
<image :src="item.image" style="width: 200rpx; height: 160rpx; margin-right: 20rpx;"></image>
<view class="d-flex flex-fill flex-column justify-content-between" style="height: 160rpx;">
<view class="font-size-lg text-color-base">{{ item.title }}</view>
<view class="d-flex justify-content-between align-items-center">
<view class="font-size-sm">¥{{ item.amount }}</view>
<button type="primary" size="mini" plain class="pay-btn">去购买</button>
</view>
</view>
</view>
</view>
<!-- 底部 -->
<view
class="d-flex position-fixed bg-base fixed-bottom text-color-primary font-size-base align-items-center just-content-center w-100"
style="height: 150rpx;">
<text>购买记录</text>
</view>
</view>
<loading v-else></loading>
</template>
<script>
import loading from '@/components/loading'
export default {
components: {
loading
},
data() {
return {
loading: true, //是否在异步获取数据
packages: [] //券包数据对象
}
},
async onLoad() {
await this.getPackages()
},
async onPullDownRefresh() {
this.packages = []//下拉刷新
await this.getPackages()//获取数据
// 当刷新操作完成后,停止下拉刷新效果
uni.stopPullDownRefresh();
},
methods: {
async getPackages() {
this.loading = true
setTimeout(() => {
// 这里是2秒后需要执行的代码
this.$api('packages').then((result) => {
this.packages = result
console.log(result); // 输出 '数据'
}); //调用api,获取券包数据
this.loading = false
}, 500);
},
}
}
</script>
<style>
.container {
padding-bottom: -150rpx;
}
.pay-btn {
height: 50rpx;
width: 120rpx;
font-size: $font-size-sm;
border-radius: 50rpx;
padding: 0;
line-height: 50rpx;
text-align: center;
}
</style>
★3、通过在packages.vue界面中通过对每个优惠券item设置点击事件 @tap="pay(item.id)",跳转时传入id参数到进到“购买券包”界面,该界面在packages文件夹下命名为detail.vue,并设置stlye,样式如下。注意这里是在同级文件夹下创建页面。
"path" : "pages/packages/detail",
"style" :
{
"navigationBarTitleText": "购买劵包",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff"
}
}
按照UI设计师效果图编写“购买券包”界面,界面分为三个模块研发,如图3.4.11。第一模块为一张图片,可以使用CSS的视口单位vh来设置元素的高度。vh单位表示视口高度的百分比,100vh就是整个视口的高度,图片设置height: 40vh。第二模块为优惠券详细列表,通过调用this.$api('packages')获取所有优惠券列表,“券包商品”界面跳转至本页面传入了id参数,利用id参数过滤找到本券包的优惠券数据信息。
<!-- 过滤数据 -->
this.package = packages.filter(item => item.id == option.id)[0]; //过滤数据
获取到package对象中的coupons为优惠券数据,使用v-for对coupons循环操作可完成列表展示。其中,coupon.detail.image为优惠券图片,coupon.coupon_num为优惠券数量,coupon.detail.coupon_title为优惠券作用,coupon.detail.expire为领券当日开始有效期,coupon.detail.coupon_use_time[0].use_time_start、use_time_end为使用开始时段和结束时段,如图3.4.12。第三模块为用户须知信息提示,使用view完成文字提示即可,代码如下:
<view class="font-size-extra-lg" style="margin-bottom: 40rpx;">购买须知</view>
<view class="font-size-base text-color-base">
售卖时间:{{ package.start_at.split(' ')[0] }}~{{ package.end_at.split(' ')[0] }}
</view>
<view class="font-size-base text-color-base" style="margin-bottom: 30rpx;">
购买限制:无限制
</view>
<view class="font-size-sm text-color-assist pre-line">
{{ package.content }}
</view>
完成三个模块的界面搭建后,最终效果如图3.4.3。
每个优惠券列表通过@tap="openCouponDetailModal(coupon)"完成交互,点击后弹出modal框做优惠券详细信息介绍,效果图如图3.4.13。引入'@/components/modal/modal'组件,通过couponDetailModalShow布尔参数判断modal框的显示与消失。部分代码如下:
<modal custom :show="couponDetailModalShow" @cancel="closeCouponDetailModal">
<view class="d-flex flex-column">
<view class="text-center font-size-extra-lg text-color-base" style="margin-bottom: 30rpx;">
{{ coupon.detail.coupon_title }}
</view>
<view class="text-center font-size-sm text-color-assist" style="margin-bottom: 40rpx;">
{{ coupon.detail.expire }}
</view>
<view class="text-color-assist font-size-sm pre-line">
{{ coupon.detail.desc }}
</view>
</view>
</modal>
packages.vue跳转代码
...
...
<!-- 卡片 -->
<view class="d-flex flex-column w-100" style="padding: 30rpx; padding-bottom: -34rpx; margin-bottom: 150rpx;">
<view class="d-flex align-items-center bg-white"
style="padding: 30rpx; height: 220rpx; margin-bottom: 34rpx; border-radius: 8rpx;"
v-for="(item, index) in packages" :key="index" @tap="pay(item.id)">
<image :src="item.image" style="width: 200rpx; height: 160rpx; margin-right: 20rpx;"></image>
...
...
...
methods: {
...
...
...
pay(id) {
uni.navigateTo({
url: '/pages/packages/detail?id=' + id
})
}
}
}
“购买券包”界面detail.vue
<template>
<!-- 购买劵包 -->
<view class="w-100 h-100">
<!-- 上方图片 -->
<image :src="package.image" class="w-100" style="height: 40vh;"></image>
<view style="padding: 30rpx 40rpx; padding-bottom: 100rpx;">
<view class="d-flex justify-content-between align-items-center" style="margin-bottom: 20rpx;">
<view class="font-size-lg">{{ package.title }}</view>
<view class="text-color-primary font-size-sm">购买记录</view>
</view>
<view class="text-color-assist font-size-sm" style="margin-bottom: 20rpx;">
共{{couponNum}}张优惠券
</view>
<!-- 优惠券列表 begin -->
<view class="d-flex flex-column w-100">
<view class="coupon d-flex flex-column bg-white" v-for="(coupon, index) in package.coupons" :key="index"
@tap="openCouponDetailModal(coupon)">
<view class="d-flex flex-fill overflow-hidden" style="margin-bottom: 20rpx;">
<image :src="coupon.detail.image" style="margin-right: 40rpx;width: 150rpx; height: 150rpx;">
</image>
<view class="flex-fill flex-column justify-content-start overflow-hidden">
<view class="text-right text-color-assist">x{{ coupon.coupon_num }}</view>
<view class="text-truncate font-size-extra-lg text-color-base w-80">
{{ coupon.detail.coupon_title }}
</view>
<view class="font-size-sm text-color-assist">{{ coupon.detail.expire }}</view>
</view>
</view>
<view class="bottom d-flex font-size-sm justify-content-between align-items-center">
<view class="text-color-assist">
使用时段:{{ coupon.detail && coupon.detail.coupon_use_time[0].use_time_start}}-{{coupon.detail && coupon.detail.coupon_use_time[0].use_time_end}}
</view>
<view class="text-color-primary">
查看详情
</view>
</view>
</view>
</view>
<!-- 优惠券列表 end -->
<view class="font-size-extra-lg" style="margin-bottom: 40rpx;">购买须知</view>
<view class="font-size-base text-color-base">
售卖时间:{{ package.start_at.split(' ')[0] }}~{{ package.end_at.split(' ')[0] }}
</view>
<view class="font-size-base text-color-base" style="margin-bottom: 30rpx;">
购买限制:无限制
</view>
<view class="font-size-sm text-color-assist pre-line">
{{ package.content }}
</view>
</view>
<!-- 下方按钮 -->
<view class="pay-box d-flex just-content-center align-items-center position-fixed fixed-bottom bg-white">
<button type="primary" class="pay-btn font-size-base text-color-white rounded-pill"
style="width: 90%;height: 80rpx; line-height: 80rpx;">
¥{{ package.amount }}购买
</button>
</view>
<modal custom :show="couponDetailModalShow" @cancel="closeCouponDetailModal">
<view class="d-flex flex-column">
<view class="text-center font-size-extra-lg text-color-base" style="margin-bottom: 30rpx;">
{{ coupon.detail.coupon_title }}
</view>
<view class="text-center font-size-sm text-color-assist" style="margin-bottom: 40rpx;">
{{ coupon.detail.expire }}
</view>
<view class="text-color-assist font-size-sm pre-line">
{{ coupon.detail.desc }}
</view>
</view>
</modal>
</view>
</template>
<script>
import modal from '@/components/modal/modal'
export default {
components: {
modal
},
data() {
return {
coupon: {
detail: {} //用这个对象来装
},
package: {
start_at: '', //将时间类型转化字符串
end_at: '' //将时间类型转化字符串
},
couponDetailModalShow: false
};
},
computed: {
couponNum() { //求和,通过对象
return this.package.coupons && this.package.coupons.reduce((acc, coupon) => acc + coupon.coupon_num,
0) //计算和操作数组元素
}
},
async onLoad(option) {
const packages = await this.$api('packages');
this.package = packages.filter(item => item.id == option.id)[0]; //过滤数据
},
methods: {
openCouponDetailModal(coupon) { //dialog点击
this.coupon = coupon
this.couponDetailModalShow = true
},
closeCouponDetailModal() { //dialog关闭
this.couponDetailModalShow = false
}
}
}
</script>
<style lang="scss" scoped>
.coupon {
border-radius: 6rpx;
padding: 20rpx 40rpx;
margin-bottom: 40rpx;
box-shadow: $box-shadow;
position: relative;
&::before {
content: " ";
position: absolute;
background-color: $bg-color;
width: 30rpx;
height: 30rpx;
bottom: 70rpx;
left: -15rpx;
border-radius: 100%;
}
&::after {
content: " ";
position: absolute;
background-color: $bg-color;
width: 30rpx;
height: 30rpx;
bottom: 70rpx;
right: -15rpx;
border-radius: 100%;
}
.bottom {
height: 70rpx;
position: relative;
&::before {
content: '';
border-top: 2rpx dashed #E2E2E2;
position: absolute;
left: 0;
top: 0;
right: 0;
transform: scaleY(0.2);
}
}
}
.pay-box {
padding: 10rpx 0;
height: 100rpx;
background-color: #00000000;
}
</style>
4、在完成本任务工单的研发工作后,团队成员应使用SourceTree工具执行版本提交,以创建此工单研发代码的历史版本记录。