任务2-2操作步骤:点餐配送界面研发
2024-11-30 本文已影响0人
吴国友
任务二工单2:点餐配送界面
1、效果图决定分为三个板块进行研发。由于本页面比较复杂涉及样式较多,在/menu下新建menu.scss文件单独存放样式,如图,通过menu.vue文件中的style中引入。
1.png
2.2.2 点餐界面区域板块.png
2、对该页面上方板块进行布局,注意中间样式运用,完成效果如图。
3.png
menu.vue代码如下:
<template>
<view class="container">
<view class="main">
<!-- 头部 -->
<view class="nav">
<view class="header">
<!-- 未接单 -->
<view class="left">
<view class="store-name">
<text>天府大悦城ONE LIFE店</text>
<view class="iconfont iconarrow-right"></view>
</view>
<view class="store-location">
<image src='/static/images/order/location.png' style="width: 30rpx; height: 30rpx;"
class="mr-10"></image>
<text>距离您 996 米</text>
</view>
</view>
<!-- 已接单 -->
<view class="right">
<view class="dinein" :class="{active: 'takein' == 'takein'}">
<text>自取</text>
</view>
<view class="takeout" :class="{active: 'takein' == 'takeout'}">
<text>外卖</text>
</view>
</view>
</view>
<view class="coupon">
<text class="title">"霸气mini卡"超级购券活动,赶紧去购买</text>
<view class="iconfont iconarrow-right"></view>
</view>
</view>
<!-- 中间左侧 -->
<!-- 中间右侧 -->
</view>
</view>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import '~@/pages/menu/menu.scss';
</style>
menu.scss代码如下:
/* 头部 */
/* #ifdef H5 */
page {
min-height: 100%;
}
/* #endif */
.container {
overflow: hidden;
position: relative;
}
.main {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
}
.nav {
width: 100%;
height: 212rpx;
flex-shrink: 0;
display: flex;
flex-direction: column;
.header {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx;
background-color: #ffffff;
height: 140rpx;
.left {
flex: 1;
display: flex;
flex-direction: column;
.store-name {
display: flex;
justify-content: flex-start;
align-items: center;
font-size: $font-size-lg;
margin-bottom: 10rpx;
.iconfont {
margin-left: 10rpx;
line-height: 100%;
}
}
.store-location {
display: flex;
justify-content: flex-start;
align-items: center;
color: $text-color-assist;
font-size: $font-size-sm;
.iconfont {
vertical-align: middle;
display: table-cell;
color: $color-primary;
line-height: 100%;
}
}
}
.right {
background-color: $bg-color-grey;
border-radius: 38rpx;
display: flex;
align-items: center;
font-size: $font-size-sm;
padding: 0 38rpx;
color: $text-color-assist;
.dinein, .takeout {
position: relative;
display: flex;
align-items: center;
&.active {
padding: 14rpx 38rpx;
color: #ffffff;
background-color: $color-primary;
border-radius: 38rpx;
}
}
.takeout {
margin-left: 20rpx;
height: 100%;
flex: 1;
padding: 14rpx 0;
}
.dinein.active {
margin-left: -38rpx;
}
.takeout.active {
margin-right: -38rpx;
}
}
}
.coupon {
flex: 1;
width: 100%;
background-color: $bg-color-primary;
font-size: $font-size-base;
color: $color-primary;
padding: 0 20rpx;
display: flex;
align-items: center;
overflow: hidden;
.title {
flex: 1;
margin-left: 10rpx;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.iconfont {
line-height: 100%;
}
}
}
3、对该页面下方左板块进行布局,需引入后端工程师提供的api测试数据,引入后如图,通过编写/api/index.js模拟真实网络获取数据动作,同时,在main.js文件中做所有 Vue 实例的共享方法$api。
4.后端工程师提供的api测试数据.png
main.js代码
import Vue from 'vue'
import App from './App'
import api from './api'
Vue.config.productionTip = false
App.mpType = 'app'
Vue.prototype.$api = api
const app = new Vue({
...App
})
app.$mount()
左板块饮品种类导航api数据为goods.js,通过在生命周期onLoad()方法中进行数据获取,再对布局文件循环数据填充(v-for),最终效果如图:
5.页面左侧布局效果图.png
menu.vue代码如下:
<template>
<view class="container">
<view class="main">
<!-- 头部 -->
<view class="nav">
<view class="header">
<!-- 未接单 -->
<view class="left">
<view class="store-name">
<text>天府大悦城ONE LIFE店</text>
<view class="iconfont iconarrow-right"></view>
</view>
<view class="store-location">
<image src='/static/images/order/location.png' style="width: 30rpx; height: 30rpx;"
class="mr-10"></image>
<text>距离您 996 米</text>
</view>
</view>
<!-- 已接单 -->
<view class="right">
<view class="dinein" :class="{active: 'takein' == 'takein'}">
<text>自取</text>
</view>
<view class="takeout" :class="{active: 'takein' == 'takeout'}">
<text>外卖</text>
</view>
</view>
</view>
<view class="coupon">
<text class="title">"霸气mini卡"超级购券活动,赶紧去购买</text>
<view class="iconfont iconarrow-right"></view>
</view>
</view>
<!-- 中间左侧 -->
<view class="content">
<scroll-view class="menus" scroll-with-animation scroll-y>
<view class="wrapper">
<view class="menu" :id="`menu-${item.id}`" :class="{'current': item.id === currentCateId}" v-for="(item, index) in goods"
:key="index">
<text>{{ item.name }}</text>
</view>
</view>
</scroll-view>
</view>
<!-- 中间右侧 -->
</view>
</view>
</template>
<script>
export default {
data() {
return {
goods: [], //所有商品
currentCateId: 6905, //默认分类
}
},
async onLoad() {
await this.init() //在生命周期调用初始化页面
},
methods: {
async init() { //页面初始化方法
this.goods = await this.$api('goods') //对goods变量进行货物数据填充
}
}
}
</script>
<style lang="scss" scoped>
@import '~@/pages/menu/menu.scss';
</style>
menu.scss代码如下:
/* 头部 */
/* #ifdef H5 */
page {
min-height: 100%;
}
/* #endif */
.container {
overflow: hidden;
position: relative;
}
.main {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
}
.nav {
width: 100%;
height: 212rpx;
flex-shrink: 0;
display: flex;
flex-direction: column;
.header {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx;
background-color: #ffffff;
height: 140rpx;
.left {
flex: 1;
display: flex;
flex-direction: column;
.store-name {
display: flex;
justify-content: flex-start;
align-items: center;
font-size: $font-size-lg;
margin-bottom: 10rpx;
.iconfont {
margin-left: 10rpx;
line-height: 100%;
}
}
.store-location {
display: flex;
justify-content: flex-start;
align-items: center;
color: $text-color-assist;
font-size: $font-size-sm;
.iconfont {
vertical-align: middle;
display: table-cell;
color: $color-primary;
line-height: 100%;
}
}
}
.right {
background-color: $bg-color-grey;
border-radius: 38rpx;
display: flex;
align-items: center;
font-size: $font-size-sm;
padding: 0 38rpx;
color: $text-color-assist;
.dinein, .takeout {
position: relative;
display: flex;
align-items: center;
&.active {
padding: 14rpx 38rpx;
color: #ffffff;
background-color: $color-primary;
border-radius: 38rpx;
}
}
.takeout {
margin-left: 20rpx;
height: 100%;
flex: 1;
padding: 14rpx 0;
}
.dinein.active {
margin-left: -38rpx;
}
.takeout.active {
margin-right: -38rpx;
}
}
}
.coupon {
flex: 1;
width: 100%;
background-color: $bg-color-primary;
font-size: $font-size-base;
color: $color-primary;
padding: 0 20rpx;
display: flex;
align-items: center;
overflow: hidden;
.title {
flex: 1;
margin-left: 10rpx;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.iconfont {
line-height: 100%;
}
}
}
.content {
flex: 1;
overflow: hidden;
width: 100%;
display: flex;
/* 下方left */
.menus {
width: 200rpx;
height: 100%;
overflow: hidden;
background-color: $bg-color-grey;
.wrapper {
width: 100%;
height: 100%;
.menu {
display: flex;
align-items: center;
justify-content: flex-start;
padding: 30rpx 20rpx;
font-size: 26rpx;
color: $text-color-assist;
position: relative;
&:nth-last-child(1) {
margin-bottom: 130rpx;
}
&.current {
background-color: #ffffff;
color: $text-color-base;
}
.dot {
position: absolute;
width: 34rpx;
height: 34rpx;
line-height: 34rpx;
font-size: 22rpx;
background-color: $color-primary;
color: #ffffff;
top: 16rpx;
right: 10rpx;
border-radius: 100%;
text-align: center;
}
}
}
}
}
4、右板块为详细商品,每一个左侧种类会关联部分商品。布局结构上为scroll-view滑动列表,滑动列表上方为5张轮播图,下方为每类商品详细信息、商品数量增减功能。
先对上方轮播图进行布局,通过uni-app框架swiper组件(轮播组件)绑定ads数组(轮播图图片数组)进行循环渲染,如图。
6. 循环处理轮播图数据.png
menu.vue 轮播图代码如下:
<!-- 中间右侧 -->
<scroll-view class="goods" scroll-with-animation scroll-y>
<view class="wrapper">
<!-- 轮播图 -->
<swiper class="ads" id="ads" autoplay :interval="3000" indicator-dots>
<swiper-item v-for="(item, index) in ads" :key='index'>
<image :src="item.image"></image>
</swiper-item>
</swiper>
</view>
</scroll-view>
......
......
......
data() {
return {
goods: [], //所有商品
currentCateId: 6905, //默认分类
ads:[{image: 'https://s3.uuu.ovh/imgs/2025/04/06/66a62739b6bb6c6e.png'},
{image: 'https://s3.uuu.ovh/imgs/2025/04/06/5202f8ff24613d35.png'},
{image: 'https://s3.uuu.ovh/imgs/2025/04/06/96228fabd6c0610c.png'},
{image: 'https://s3.uuu.ovh/imgs/2025/04/06/a69f605a1dcc3496.png'},
{image: 'https://s3.uuu.ovh/imgs/2025/04/06/9a0632bfa5685857.png'}
], //轮播图数据
}
},
......
......
......
menu.scss 新增样式如下(在conent中新增):
......
......
......
.goods {
flex: 1;
height: 100%;
overflow: hidden;
background-color: #ffffff;
.wrapper {
width: 100%;
height: 100%;
padding: 20rpx;
.ads {
height: calc(300 / 550 * 510rpx);
image {
width: 100%;
height: 100%;
border-radius: 8rpx;
}
}
}
}
效果如图
7.gif
5、其次,对下方商品列表使用弹性布局循环搭建布局,从goods.js中获得数据,通过goods_list(商品详细列表)对下方商品详情进行数据绑定,效果如图2.2.8。json数据解析如下:
"sort": 1,//排序
"icon": "",//商品系列图标
"id": 6905,//商品系列id
"goods_list": [{//商品列表,为数组,多个商品
"sell_time_status": 0,
"id": 65825,//商品id
"is_sell": true,//是否打折
"pack_cost": "0.00",
"sales": 487,
"goods_type": 1,//商品类型 1为普通,2为可选规格
"cover_img": "",
...
...
"sort": 99,
"price": 18.5,//商品价格
"unit": "件",//单位
...
...
"specs": [],
"content": "购买三明治,享早餐指定饮品半价",//商品描述
...
...
"name": "云鲤早餐",//商品名称
"images": "https://s3.uuu.ovh/imgs/2025/04/06/3ecbb3b41c451e89.png"//商品图片
}],
"name": "云鲤早餐",//商品系列名称
"is_show_backstage": 0
}
menu.vue 本工单所有代码如下:
<template>
<view class="container">
<view class="main">
<!-- 头部 -->
<view class="nav">
<view class="header">
<!-- 未接单 -->
<view class="left">
<view class="store-name">
<text>天府大悦城ONE LIFE店</text>
<view class="iconfont iconarrow-right"></view>
</view>
<view class="store-location">
<image src='/static/images/order/location.png' style="width: 30rpx; height: 30rpx;"
class="mr-10"></image>
<text>距离您 996 米</text>
</view>
</view>
<!-- 已接单 -->
<view class="right">
<view class="dinein" :class="{active: 'takein' == 'takein'}">
<text>自取</text>
</view>
<view class="takeout" :class="{active: 'takein' == 'takeout'}">
<text>外卖</text>
</view>
</view>
</view>
<view class="coupon">
<text class="title">"霸气mini卡"超级购券活动,赶紧去购买</text>
<view class="iconfont iconarrow-right"></view>
</view>
</view>
<!-- 中间左侧 -->
<view class="content">
<scroll-view class="menus" scroll-with-animation scroll-y>
<view class="wrapper">
<view class="menu" :id="`menu-${item.id}`" :class="{'current': item.id === currentCateId}"
v-for="(item, index) in goods" :key="index">
<text>{{ item.name }}</text>
</view>
</view>
</scroll-view>
<!-- 中间右侧 -->
<scroll-view class="goods" scroll-with-animation scroll-y>
<view class="wrapper">
<!-- 轮播图 -->
<swiper class="ads" id="ads" autoplay :interval="3000" indicator-dots>
<swiper-item v-for="(item, index) in ads" :key='index'>
<image :src="item.image"></image>
</swiper-item>
</swiper>
<!-- 商品列表 -->
<view class="list">
<!-- category begin -->
<view class="category" v-for="(item, index) in goods" :key="index" :id="`cate-${item.id}`">
<!-- 大标题 系列 -->
<view class="title">
<text>{{ item.name }}</text>
<image :src="item.icon" class="icon"></image>
</view>
<view class="items">
<!-- 每一个商品 begin -->
<view class="good" v-for="(good, key) in item.goods_list" :key="key">
<image :src="good.images" class="image" ></image>
<view class="right">
<text class="name">{{ good.name }}</text>
<text class="tips">{{ good.content }}</text>
<view class="price_and_action">
<text class="price">¥{{ good.price }}</text>
<!-- 选规格 use_property 1 -->
<view class="btn-group" v-if="good.use_property">
<button type="primary" class="btn property_btn" hover-class="none"
size="mini" >
选规格
</button>
</view>
<!-- 无选规格 use_property 0 正常增减数量-->
<view class="btn-group" v-else>
<button type="default" plain class="btn reduce_btn" size="mini" hover-class="none" >
<view class="iconfont iconsami-select"></view>
</button>
<view class="number" >1</view>
<button type="primary" class="btn add_btn" size="min" hover-class="none" >
<view class="iconfont iconadd-select"></view>
</button>
</view>
</view>
</view>
</view>
<!-- 商品 end -->
</view>
</view>
<!-- category end -->
</view>
</view>
</scroll-view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
goods: [], //所有商品
currentCateId: 6905, //默认分类
ads: [{image: 'https://s3.uuu.ovh/imgs/2025/04/06/66a62739b6bb6c6e.png'},
{image: 'https://s3.uuu.ovh/imgs/2025/04/06/5202f8ff24613d35.png'},
{image: 'https://s3.uuu.ovh/imgs/2025/04/06/96228fabd6c0610c.png'},
{image: 'https://s3.uuu.ovh/imgs/2025/04/06/a69f605a1dcc3496.png'},
{image: 'https://s3.uuu.ovh/imgs/2025/04/06/9a0632bfa5685857.png'}
], //轮播图数据
}
},
async onLoad() {
await this.init() //在生命周期调用初始化页面
},
methods: {
async init() { //页面初始化方法
this.goods = await this.$api('goods') //对goods变量进行货物数据填充
}
}
}
</script>
<style lang="scss" scoped>
@import '~@/pages/menu/menu.scss';
</style>
menu.scss 本工单所有样式如下:
/* 头部 */
/* #ifdef H5 */
page {
min-height: 100%;
}
/* #endif */
.container {
overflow: hidden;
position: relative;
}
.main {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
}
.nav {
width: 100%;
height: 212rpx;
flex-shrink: 0;
display: flex;
flex-direction: column;
.header {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx;
background-color: #ffffff;
height: 140rpx;
.left {
flex: 1;
display: flex;
flex-direction: column;
.store-name {
display: flex;
justify-content: flex-start;
align-items: center;
font-size: $font-size-lg;
margin-bottom: 10rpx;
.iconfont {
margin-left: 10rpx;
line-height: 100%;
}
}
.store-location {
display: flex;
justify-content: flex-start;
align-items: center;
color: $text-color-assist;
font-size: $font-size-sm;
.iconfont {
vertical-align: middle;
display: table-cell;
color: $color-primary;
line-height: 100%;
}
}
}
.right {
background-color: $bg-color-grey;
border-radius: 38rpx;
display: flex;
align-items: center;
font-size: $font-size-sm;
padding: 0 38rpx;
color: $text-color-assist;
.dinein, .takeout {
position: relative;
display: flex;
align-items: center;
&.active {
padding: 14rpx 38rpx;
color: #ffffff;
background-color: $color-primary;
border-radius: 38rpx;
}
}
.takeout {
margin-left: 20rpx;
height: 100%;
flex: 1;
padding: 14rpx 0;
}
.dinein.active {
margin-left: -38rpx;
}
.takeout.active {
margin-right: -38rpx;
}
}
}
.coupon {
flex: 1;
width: 100%;
background-color: $bg-color-primary;
font-size: $font-size-base;
color: $color-primary;
padding: 0 20rpx;
display: flex;
align-items: center;
overflow: hidden;
.title {
flex: 1;
margin-left: 10rpx;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.iconfont {
line-height: 100%;
}
}
}
.content {
flex: 1;
overflow: hidden;
width: 100%;
display: flex;
/* 下方left */
.menus {
width: 200rpx;
height: 100%;
overflow: hidden;
background-color: $bg-color-grey;
.wrapper {
width: 100%;
height: 100%;
.menu {
display: flex;
align-items: center;
justify-content: flex-start;
padding: 30rpx 20rpx;
font-size: 26rpx;
color: $text-color-assist;
position: relative;
&:nth-last-child(1) {
margin-bottom: 130rpx;
}
&.current {
background-color: #ffffff;
color: $text-color-base;
}
.dot {
position: absolute;
width: 34rpx;
height: 34rpx;
line-height: 34rpx;
font-size: 22rpx;
background-color: $color-primary;
color: #ffffff;
top: 16rpx;
right: 10rpx;
border-radius: 100%;
text-align: center;
}
}
}
}
/* 下方right */
.goods {
flex: 1;
height: 100%;
overflow: hidden;
background-color: #ffffff;
.wrapper {
width: 100%;
height: 100%;
padding: 20rpx;
/* 轮播图 */
.ads {
height: calc(300 / 550 * 510rpx);
image {
width: 100%;
height: 100%;
border-radius: 8rpx;
}
}
/* 商品列表 */
.list {
width: 100%;
font-size: $font-size-base;
padding-bottom: 130rpx;
.category {
width: 100%;
.title {
padding: 30rpx 0;
display: flex;
align-items: center;
color: $text-color-base;
.icon {
width: 38rpx;
height: 38rpx;
margin-left: 10rpx;
}
}
}
.items {
display: flex;
flex-direction: column;
padding-bottom: -30rpx;
.good {
display: flex;
align-items: center;
margin-bottom: 30rpx;
.image {
width: 160rpx;
height: 160rpx;
margin-right: 20rpx;
border-radius: 8rpx;
}
.right {
flex: 1;
height: 160rpx;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: space-between;
padding-right: 14rpx;
.name {
font-size: $font-size-base;
margin-bottom: 10rpx;
}
.tips {
width: 100%;
height: 40rpx;
line-height: 40rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: $font-size-sm;
color: $text-color-assist;
margin-bottom: 10rpx;
}
.price_and_action {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.price {
font-size: $font-size-base;
font-weight: 600;
}
.btn-group {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
.btn {
padding: 0 20rpx;
box-sizing: border-box;
font-size: $font-size-sm;
height: 44rpx;
line-height: 44rpx;
&.property_btn {
border-radius: 24rpx;
}
&.add_btn,
&.reduce_btn {
padding: 0;
width: 44rpx;
border-radius: 44rpx;
}
}
.dot {
position: absolute;
background-color: #ffffff;
border: 1px solid $color-primary;
color: $color-primary;
font-size: $font-size-sm;
width: 36rpx;
height: 36rpx;
line-height: 36rpx;
text-align: center;
border-radius: 100%;
right: -12rpx;
top: -10rpx;
}
.number {
width: 44rpx;
height: 44rpx;
line-height: 44rpx;
text-align: center;
}
}
}
}
}
}
}
}
}
}
6、团队成员对本任务工单研发完成后,需通过SourceTree工具进行版本提交,如图2.2.9,对研发代码维护形成历史版本,以便远程备份、版本切换等。
8.png