Browse Source

feat: 核销页面;

pull/1/head
fox 2 months ago
parent
commit
0ca8332e4d
9 changed files with 433 additions and 192 deletions
  1. +185
    -0
      components/order/orderCard.vue
  2. +29
    -2
      components/product/productCard.vue
  3. +8
    -3
      pages.json
  4. +0
    -160
      pages/index/order.vue
  5. +1
    -27
      pages_order/order/createOrder.vue
  6. +210
    -0
      pages_order/order/verifyOrder.vue
  7. BIN
      pages_order/static/verifyOrder/icon-checked.png
  8. BIN
      pages_order/static/verifyOrder/icon-clock.png
  9. BIN
      pages_order/static/verifyOrder/temp-qrcode.png

+ 185
- 0
components/order/orderCard.vue View File

@ -0,0 +1,185 @@
<template>
<view class="card order order-card__view">
<view class="flex overview">
<text class="title">{{ data.title }}</text>
<text class="state">{{ stateCodeAndDescFieldsMapping[data.state] }}</text>
</view>
<view class="flex detail">
<image class="img" :src="data.imgUrl"></image>
<view class="info">
<view class="desc">
<view class="row">{{ `服务内容:${data.desc || '-'}` }}</view>
<view class="row">{{ `下单时间:${data.createTime || '-'}` }}</view>
<view class="row">{{ `订单号:${data.orderNo || '-'}` }}</view>
</view>
<view class="price">
总价格<text class="unit">¥</text><text class="count">{{ data.price }}</text>
</view>
<view class="btns" v-if="[0, 1].includes(data.state)">
<!-- 待付款 -->
<template v-if="data.state === 0">
<button plain class="btn btn-plain" @click="onCancel">取消订单</button>
<button plain class="btn" @click="onPay">立即付款</button>
</template>
<!-- 待核销 -->
<template v-if="data.state === 1">
<button plain class="btn" @click="onVerify">去核销</button>
</template>
</view>
</view>
</view>
</view>
</template>
<script>
import mixinsOrder from '@/mixins/order.js'
const TEST_DATA = {
id: '001',
imgUrl: '../../static/image/home/temp-product.png',
title: '60分钟肩颈推拿按摩',
desc: '疏通经络 放松肌肉',
price: 264,
orderNo: 'da123567',
createTime: '2024-12-24 18:45:23',
state: 1,
}
export default {
mixins: [mixinsOrder],
props: {
data: {
type: Object,
default() {
// todo: delete
return TEST_DATA
}
},
},
data() {
return {
stateCodeAndDescFieldsMapping: {
0: '待付款',
1: '待核销',
2: '已完成',
3: '已取消',
}
}
},
methods: {
onCancel() {
this.cancelOrder(this.data)
},
onPay() {
this.toPayOrder(this.data)
},
onVerify() {
// todo
this.$utils.navigateTo(`/pages_order/order/verifyOrder?id=${this.data.id}`)
}
},
}
</script>
<style scoped lang="scss">
.order {
background-color: $uni-fg-color;
padding: 15rpx 33rpx 0 30rpx;
& + & {
margin-top: 20rpx;
}
}
.overview {
justify-content: space-between;
font-size: 28rpx;
.title {
color: #000000;
font-weight: 700;
}
.state {
color: $uni-color-light;
}
}
.detail {
margin-top: 9rpx;
align-items: flex-start;
.img {
width: 167rpx;
height: 167rpx;
margin-right: 15rpx;
margin-bottom: 29rpx;
}
}
.info {
flex: 1;
font-size: 22rpx;
.desc {
background-color: #F5F5F5;
border-radius: 5rpx;
padding: 13rpx 16rpx;
color: #999999;
}
.row + .row {
margin-top: 6rpx;
}
.price {
margin-top: 8rpx;
text-align: right;
color: #000000;
.count,
.unit {
color: #FF2A2A;
}
.unit {
font-size: 18rpx;
}
}
.btns {
margin-top: 16rpx;
margin-bottom: 9rpx;
text-align: right;
font-size: 0;
}
}
.btn {
display: inline-block;
width: auto;
min-width: 145rpx;
height: auto;
padding: 14rpx 28rpx;
box-sizing: border-box;
border: none;
border-radius: 29rpx;
margin: 0;
font-size: 22rpx;
line-height: 1;
color: #FFFFFF;
background-image: linear-gradient(to right, #84A73F, #D8FF8F);
& + & {
margin-left: 39rpx;
}
&-plain {
border: 2rpx solid #999999;
color: #999999;
background: none;
}
}
</style>

+ 29
- 2
components/product/productCard.vue View File

@ -1,5 +1,5 @@
<template> <template>
<view class="card flex product product-card__view" :class="[direction]" @click="$emit('click')">
<view class="card flex product product-card__view" :class="[direction, size]" @click="$emit('click')">
<view class="img" :class="[ data.isRecommend ? 'is-recommend' : '']"> <view class="img" :class="[ data.isRecommend ? 'is-recommend' : '']">
<image :src="data.imgUrl" mode="aspectFill"></image> <image :src="data.imgUrl" mode="aspectFill"></image>
<image v-if="data.isRecommend" class="mark" src="@/static/image/home/mark-recommend.png"></image> <image v-if="data.isRecommend" class="mark" src="@/static/image/home/mark-recommend.png"></image>
@ -59,10 +59,14 @@
type: String, type: String,
default: 'horizontal' // horizontal | vertical default: 'horizontal' // horizontal | vertical
}, },
size: {
type: String,
default: 'small' // small | medium
},
readonly: { readonly: {
type: Boolean, type: Boolean,
default: false, default: false,
}
},
}, },
data() { data() {
return { return {
@ -219,6 +223,29 @@
border-radius: 10rpx; border-radius: 10rpx;
} }
} }
&.medium {
padding: 27rpx 25rpx;
width: 100%;
box-sizing: border-box;
.img {
width: 184rpx;
height: 184rpx;
}
.title {
margin-bottom: 14rpx;
}
.price {
margin-top: 15rpx;
}
.sales {
margin-top: 15rpx;
}
}
} }
</style> </style>

+ 8
- 3
pages.json View File

@ -52,7 +52,15 @@
{ {
"path": "product/productDetail" "path": "product/productDetail"
}, },
{
"path": "order/createOrder"
},
{
"path": "order/verifyOrder"
},
{ {
"path": "mine/partner" "path": "mine/partner"
}, },
@ -92,9 +100,6 @@
{ {
"path": "home/journalism" "path": "home/journalism"
}, },
{
"path": "order/createOrder"
},
{ {
"path": "mine/balance" "path": "mine/balance"
}, },


+ 0
- 160
pages/index/order.vue View File

@ -36,78 +36,7 @@
<!-- todo: delete --> <!-- todo: delete -->
<orderCard></orderCard> <orderCard></orderCard>
</view> </view>
<template v-if="false">
<view class="list">
<view class="item" v-for="(item, index) in list" @click="toOrderDetail(item.id)" :key="index">
<view class="content" :key="index" v-for="(good, index) in item.commonOrderSkuList">
<view class="top">
<view class="service">
{{ good.title }}
</view>
<view class="status">
<text> {{ tabs[Number(item.state) + 1].name }}</text>
</view>
</view>
<view class="main">
<view class="left">
<image mode="aspectFill" :src="good.image && good.image.split(',')[0]"></image>
</view>
<view class="right">
<view class="text-hidden-1">
客户姓名{{item.name}}
</view>
<view class="text-hidden-1">
下单时间{{item.createTime}}
</view>
<view class="text-hidden-1">
联系电话{{item.phone}}
</view>
</view>
</view>
</view>
<view class="bottom">
<view class="price">
<text class="total-title">总价格</text>
<text class="unit"></text>
<text class="num">{{item.price}}</text>
<text class="c-unit"></text>
</view>
<view @click.stop="toPayOrder(item)" class="btn" v-if="item.state == 0">
立即付款
</view>
<view @click.stop="cancelOrder(item)" class="btn" v-if="item.state == 0">
取消订单
</view>
<view class="btn" @click.stop="confirmOrder(item)" v-if="item.state == 2">
确认收货
</view>
<view @click.stop="$refs.customerServicePopup.open()" class="btn" v-if="item.state > 0">
联系客服
</view>
</view>
</view>
<view style="
margin-top: 20rpx;
min-width: 700rpx;">
<uv-empty mode="list" v-if="list.length == 0"></uv-empty>
</view>
</view>
</template>
<customerServicePopup ref="customerServicePopup" />
<tabber select="order" /> <tabber select="order" />
</view> </view>
</template> </template>
@ -121,14 +50,12 @@
import mixinsList from '@/mixins/list.js' import mixinsList from '@/mixins/list.js'
import mixinsOrder from '@/mixins/order.js' import mixinsOrder from '@/mixins/order.js'
import tabber from '@/components/base/tabbar.vue' import tabber from '@/components/base/tabbar.vue'
import customerServicePopup from '@/components/config/customerServicePopup.vue'
export default { export default {
mixins: [mixinsList, mixinsOrder], mixins: [mixinsList, mixinsOrder],
components: { components: {
orderCard, orderCard,
tabber, tabber,
customerServicePopup,
}, },
computed: {}, computed: {},
data() { data() {
@ -199,91 +126,4 @@
.list { .list {
padding: 16rpx 17rpx; padding: 16rpx 17rpx;
} }
.list {
.item {
width: calc(100% - 40rpx);
background-color: #fff;
margin: 20rpx;
box-sizing: border-box;
border-radius: 16rpx;
padding: 30rpx;
.content {
.top {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 34rpx;
.status {
font-weight: 600;
color: #FFAC2F;
flex-shrink: 0;
margin-left: 20rpx;
}
}
.main {
display: flex;
margin: 20rpx 0rpx;
.left {
display: flex;
align-items: center;
justify-content: center;
width: 180rpx;
height: 180rpx;
image {
width: 95%;
height: 95%;
border-radius: 10rpx;
}
}
.right {
display: flex;
flex-direction: column;
justify-content: space-between;
width: calc(100% - 200rpx);
color: #777;
font-size: 26rpx;
padding: 30rpx 20rpx;
box-sizing: border-box;
margin-left: 20rpx;
border-radius: 10rpx;
background-color: #F8F8F8;
}
}
}
.bottom {
display: flex;
justify-content: space-between;
font-size: 25rpx;
.price {
.total-title {}
.num {
font-size: 36rpx;
}
.num,
.unit,
.c-unit {
color: $uni-color;
}
}
.btn {
border: 1px solid #C7C7C7;
padding: 10rpx 20rpx;
border-radius: 40rpx;
color: #575757;
}
}
}
}
</style> </style>

+ 1
- 27
pages_order/order/createOrder.vue View File

@ -6,7 +6,7 @@
<view class="content"> <view class="content">
<!-- 商品详情 --> <!-- 商品详情 -->
<productCard :data="payOrderProduct[0]" :readonly="true"></productCard>
<productCard :data="payOrderProduct[0]" size="medium" :readonly="true"></productCard>
<view class="card payment"> <view class="card payment">
<uv-radio-group v-model="payMethod"> <uv-radio-group v-model="payMethod">
@ -223,32 +223,6 @@
box-sizing: border-box; box-sizing: border-box;
} }
/deep/ .product-card__view {
// margin: 0 13rpx;
padding: 27rpx 25rpx;
// width: calc(100vw - 13rpx*2);
width: 100%;
box-sizing: border-box;
// transform: translateY(-50%);
.img {
width: 184rpx;
height: 184rpx;
}
.title {
margin-bottom: 14rpx;
}
.price {
margin-top: 15rpx;
}
.sales {
margin-top: 15rpx;
}
}
} }
.card { .card {


+ 210
- 0
pages_order/order/verifyOrder.vue View File

@ -0,0 +1,210 @@
<template>
<view class="page">
<navbar title="核销详情" leftClick @leftClick="$utils.navigateBack" color="#fff" />
<view class="flex page-header">
<image class="icon" src="@/pages_order/static/verifyOrder/icon-clock.png" mode="widthFix"></image>
<text>待核销</text>
</view>
<view class="page-content">
<!-- 商品详情 -->
<productCard :data="productDetail" size="medium" :readonly="true"></productCard>
<view class="card rights">
<view class="flex rights-item" v-for="item in orderDetail.rights" :key="item">
<image class="rights-icon" src="@/pages_order/static/verifyOrder/icon-checked.png" mode="widthFix"></image>
<text>{{ item }}</text>
</view>
</view>
<view class="card info">
<view class="info-header">核销信息</view>
<view class="flex flex-column info-content">
<image class="info-qr" :src="orderDetail.qrCodeImgUrl" mode="widthFix"></image>
<view class="info-no">{{ `订单号:${orderDetail.orderNo}` }}</view>
<view class="info-desc">{{ `有效时间:${orderDetail.startTime}${orderDetail.endTime}` }}</view>
</view>
</view>
</view>
<!-- 下单 -->
<view class="flex bar">
<button plain class="btn btn-plain" @click="onRefund">申请退款</button>
<button plain class="btn" @click="onBuyAgain">再次购买</button>
</view>
</view>
</template>
<script>
import productCard from '@/components/product/productCard.vue'
const TEMP_BANNER_IMG_URL = 'http://gips3.baidu.com/it/u=70459541,3412285454&fm=3028&app=3028&f=JPEG&fmt=auto?w=960&h=1280'
export default {
components: {
productCard,
},
data() {
return {
// todo: fetch
productDetail: {
id: '001',
imgUrl: TEMP_BANNER_IMG_URL,
price: 99,
num: 1,
sales: 235,
title: '60分钟肩颈推拿按摩',
desc: '疏通经络 放松肌肉',
tags: ['专业技师', '舒适环境', '深度放松'],
details: '<p>这里是商品详情..............<br/>这里是商品详情..............</p>'
},
orderDetail: {
id: '001',
orderNo: 'da123567',
qrCodeImgUrl: '../static/verifyOrder/temp-qrcode.png',
startTime: '2025-03-14',
endTime: '2025-04-14',
rights: ['过期退', '随时退']
},
}
},
onLoad(option) {
const { id } = option
// todo: fetch order data & product data by order id
},
methods: {
onRefund() {
// todo
},
onBuyAgain() {
// todo: check
this.$utils.navigateTo(`/pages_order/product/productDetail?id=${this.productDetail.id}`)
},
},
}
</script>
<style scoped lang="scss">
$bar-height: 132rpx;
.page {
background-color: #F5F5F5;
/deep/ .nav-bar__view {
background-image: linear-gradient(#84A73F, #D8FF8F);
}
&-header {
color: #000000;
font-size: 28rpx;
margin-top: 24rpx;
.icon {
width: 30rpx;
height: auto;
margin-right: 17rpx;
}
}
&-content {
padding: 11rpx 13rpx;
}
}
.rights {
margin-top: 15rpx;
padding: 23rpx 48rpx;
color: #000000;
font-size: 28rpx;
&-item {
margin-right: 70rpx;
display: inline-flex;
}
&-icon {
width: 30rpx;
height: auto;
margin-right: 12rpx;
}
}
.info {
margin-top: 19rpx;
padding: 25rpx 41rpx 51rpx 41rpx;
font-size: 28rpx;
&-header {
color: #000000;
padding: 0 0 16rpx 7rpx;
border-bottom: 1rpx dashed #C7C7C7;
}
&-qr {
width: 279rpx;
height: auto;
margin-top: 57rpx;
}
&-no {
color: #84A73F;
margin-top: 16rpx;
}
&-desc {
color: #999999;
font-size: 22rpx;
margin-top: 65rpx;
}
}
.bar {
position: fixed;
bottom: 0;
left: 0;
width: 100vw;
height: $bar-height;
padding-bottom: env(safe-area-inset-bottom);
background-color: $uni-fg-color;
justify-content: flex-end;
}
.btn {
display: inline-block;
width: auto;
height: auto;
padding: 24rpx 50rpx;
box-sizing: border-box;
border: none;
border-radius: 44rpx;
margin: 0;
font-size: 28rpx;
line-height: 1;
color: #FFFFFF;
background-image: linear-gradient(to right, #84A73F, #D8FF8F);
& + & {
margin-left: 39rpx;
}
&:last-child {
margin-right: 54rpx;
}
&-plain {
border: 2rpx solid #999999;
color: #999999;
background: none;
}
}
</style>

BIN
pages_order/static/verifyOrder/icon-checked.png View File

Before After
Width: 30  |  Height: 30  |  Size: 867 B

BIN
pages_order/static/verifyOrder/icon-clock.png View File

Before After
Width: 31  |  Height: 31  |  Size: 1.3 KiB

BIN
pages_order/static/verifyOrder/temp-qrcode.png View File

Before After
Width: 279  |  Height: 279  |  Size: 28 KiB

Loading…
Cancel
Save