uni-app(优医咨询)项目实战 - 第8天
学习目标:
- 能够完成问诊订单的支付及详情
- 能够完成问诊订单删除/取消操作功能
- 能够完成药品订单的支付及查看
一、药品订单
医生诊断完毕后开具的处方中包含了治疗相关的药品,患者可以在线进行购买。
创建包含药品相关页面的分包 subpkg_medicine,分包下包含了 5 个页面,先去创建页面再添加配置。
{
"subPackages": [
{
"root": "subpkg_medicine",
"pages": [{
"path": "payment/index",
"style": {
"navigationBarTitleText": "等待支付"
}
},
{
"path": "pay_result/index",
"style": {
"navigationBarTitleText": "支付结果"
}
},
{
"path": "order_detail/index",
"style": {
"navigationBarTitleText": "订单详情"
}
},
{
"path": "order_list/index",
"style": {
"navigationBarTitleText": "订单列表"
}
},
{
"path": "timeline/index",
"style": {
"navigationBarTitleText": "查看物流",
"navigationStyle": "custom"
}
}
]
}
]
}
1.1 预支付订单
在问诊室页面中医生开具处方后点击购买药品按钮跳转到预支付订单页面。
1.1.1 页面模板
<!-- subpkg_medicine/payment/index.vue -->
<script setup></script>
<template>
<scroll-page background-color="#f6f6f6">
<view class="medicine-page">
<view class="page-header">
<view class="order-shippment">
<view class="region">
<uni-icons size="16" color="#FF7702" type="location-filled" />
广东省广州市
</view>
<view class="detail">大华区明离路科技园880号</view>
<view class="receiver">李富贵 1885138766</view>
</view>
</view>
<view class="order-shop">
优医药房
<text class="alt">优医质保 假一赔十</text>
</view>
<!-- 药品列表 -->
<view class="medicine-list">
<view class="medicine-list-item">
<image
class="medicine-cover"
src="/static/uploads/medicine-1.jpg"
mode="aspectFill"
/>
<view class="medicine-info">
<text class="name">瑞巴派特片</text>
<text class="unit symbol">24片</text>
<text class="price">¥25.00</text>
</view>
<view class="quantity">x1</view>
<view class="guide">用法用量:口服,每次1袋,每天3次,用药3天</view>
</view>
<view class="medicine-list-item">
<image
class="medicine-cover"
src="/static/uploads/medicine-2.jpg"
mode="aspectFill"
/>
<view class="medicine-info">
<text class="name">瑞巴派特片</text>
<text class="unit symbol">24片</text>
<text class="price">¥25.00</text>
</view>
<view class="quantity">x1</view>
<view class="guide">用法用量:口服,每次1袋,每天3次,用药3天</view>
</view>
</view>
<!-- 订单信息 -->
<view class="order-info">
<uni-list :border="false">
<uni-list-item title="药品金额" right-text="¥50.00" />
<uni-list-item title="运费" right-text="¥5.00" />
<uni-list-item title="优惠券" show-arrow right-text="-¥10.00" />
<uni-list-item title="实付款" right-text="¥45.00" />
</uni-list>
</view>
<!-- 底部 -->
<view class="toolbar">
<view class="total-amount">
需付款: <text class="number">¥39.00</text>
</view>
<view class="buttons">
<button class="uni-button">立即支付</button>
</view>
</view>
</view>
</scroll-page>
</template>
<style lang="scss">
@import './index.scss';
</style>
// subpkg_medicine/payment/index.scss
.medicine-page {
padding-bottom: calc(env(safe-area-inset-bottom) + 200rpx);
}
.page-header {
padding: 0 30rpx 30rpx;
// 需要一个高为300的背景图
background-color: #eaf8f6;
overflow: hidden;
.order-shippment {
line-height: 1;
padding: 30rpx;
background-color: #fff;
border-radius: 20rpx;
margin-top: 30rpx;
position: relative;
.region {
display: flex;
align-items: center;
font-size: 26rpx;
color: #6f6f6f;
}
.detail {
margin: 24rpx 0;
font-size: 34rpx;
color: #121826;
}
.receiver {
font-size: 26rpx;
color: #121826;
}
}
}
.order-shop {
padding: 20rpx 30rpx;
margin: 10rpx 0;
line-height: 1;
font-size: 32rpx;
color: #121826;
font-weight: 500;
.alt {
font-size: 24rpx;
color: #979797;
margin-left: 10rpx;
}
}
.medicine-list {
padding: 0 30rpx;
background-color: #fff;
}
.medicine-list-item {
padding: 30rpx 0;
border-bottom: 1rpx solid #ededed;
position: relative;
&:last-child {
border-bottom: none;
}
.medicine-cover {
width: 180rpx;
height: 160rpx;
float: left;
}
.medicine-info {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 160rpx;
padding-left: 20rpx;
.name {
font-size: 30rpx;
font-weight: 500;
color: #121826;
}
.unit {
color: #979797;
font-size: 28rpx;
display: flex;
align-items: center;
@include text-overflow(1);
&.symbol::before {
content: '处方药';
line-height: 1;
padding: 8rpx 10rpx;
margin-right: 10rpx;
border-radius: 4rpx;
font-size: 24rpx;
color: #fff;
background-color: #16c2a3;
}
}
.price {
color: #eb5757;
font-size: 32rpx;
}
}
.quantity {
top: 30rpx;
right: 0;
position: absolute;
font-size: 30rpx;
color: #121826;
}
.guide {
clear: both;
padding: 20rpx;
margin-top: 30rpx;
font-size: 28rpx;
color: #848484;
border-radius: 10rpx;
background-color: #f6f6f6;
}
}
.order-info {
padding: 0 30rpx;
margin-top: 30rpx;
background-color: #fff;
.right-text {
display: flex;
align-items: center;
}
.copy {
line-height: 1;
padding: 6rpx 20rpx 4rpx;
margin-right: 20rpx;
font-size: 24rpx;
color: #16c2a3;
background-color: #fafafa;
border-radius: 40rpx;
border: 1rpx solid #16c2a3;
}
.number {
font-size: 32rpx;
color: #848484;
}
:deep(.uni-list-item__container) {
padding-left: 0 !important;
padding-right: 0 !important;
}
:deep(.uni-list-item__content-title) {
font-size: 32rpx !important;
color: #3c3e42 !important;
}
:deep(.uni-list-item__extra-text) {
font-size: 32rpx !important;
color: #848484 !important;
}
:deep(.uni-icon-wrapper) {
padding: 0 !important;
margin-right: -10rpx !important;
font-size: 36rpx !important;
margin-top: 2rpx;
}
}
.toolbar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
height: 88rpx;
padding: 30rpx 40rpx calc(env(safe-area-inset-bottom) + 30rpx);
background-color: #fff;
.buttons {
flex: 1;
display: flex;
}
.uni-button {
flex: 1;
padding: 0 30rpx;
}
.total-amount {
display: flex;
align-items: center;
width: 270rpx;
font-size: 28rpx;
color: #3c3e42;
}
.number {
font-size: 40rpx;
color: #eb5757;
margin-left: 10rpx;
}
}
1.1.2 收货人地址
调用接口获取收货人地址,接口文档在这里,分成以下两步来实现:
- 封装调用接口的方法
// services/address.js
import { http } from '@/utils/http.js'
/**
* 收货地址列表
*/
export const addressListApi = () => {
return http.get('/patient/order/address')
}
- 在页面中调用接口方法获取并渲染地址信息
<!-- subpkg_medicine/payment/index.vue -->
<script setup>
import { ref } from 'vue'
import { addressListApi } from '@/services/address'
// 收货地址信息
const addressInfo = ref({})
// 用户收货地址
async function getAddressList() {
// 地址列表接口
const { code, data, message } = await addressListApi()
// 检测接口是否调用成功
if (code !== 10000) return uni.utils.toast(message)
// 渲染地址列表数据
addressInfo.value = data[0]
}
// 获取收货地址列表
getAddressList()
</script>
<template>
<scroll-page background-color="#f6f6f6">
<view class="medicine-page">
<view class="page-header">
<view class="order-shippment">
<view class="region">
<uni-icons size="16" color="#FF7702" type="location-filled" />
{{ addressInfo.city }} {{ addressInfo.county }}
</view>
<view class="detail">{{ addressInfo.addressDetail }}</view>
<view class="receiver">
{{ addressInfo.receiver }} {{ addressInfo.mobile }}
</view>
</view>
</view>
<!-- 省略前面小节的代码... -->
</view>
</scroll-page>
</template>
1.1.3 预付订单信息
调用接口获取药品预付订单信息,接口文档在这里,分成以下 x 个步骤实现:
- 封装调用接口的方法
// services/medicine.js
import { http } from '@/utils/http.js'
/**
* 药品预付订单
*/
export const preOrderApi = (prescriptionId) => {
return http.get('/patient/medicine/order/pre', { params: { prescriptionId } })
}
- 获取地址参数并调用接口方法,先在跳转地址中拼接处方的 ID
<!-- subpkg_consult/room/components/prescription-info.vue -->
<template>
<!-- 处方消息(22)-->
<view class="e-prescription">
<!-- 省略前面小节的代码... -->
<navigator
class="uni-link"
hover-class="none"
:url="`/subpkg_medicine/payment/index?id=${props.info.id}`"
>
购买药品
</navigator>
</view>
</template>
然后再获取地址中的处方 ID 并调用方法获取预付订单信息
<!-- subpkg_medicine/payment/index.vue -->
<script setup>
import { ref } from 'vue'
import { preOrderApi } from '@/services/medicine'
import { addressListApi } from '@/services/address'
// 获取地址中的参数
const props = defineProps({
id: String,
})
// 预付订单信息
const preOrderInfo = ref({})
// 收货地址信息
const addressInfo = ref({})
// 省略前面小节的代码...
// 药品预付订单
async function createPreOrder() {
if (!props.id) return
// 预付订单接口
const { code, data, message } = await preOrderApi(props.id)
// 检测接口是否调用成功
if (code !== 10000) return uni.utils.toast(message)
// 渲染预付订单信息
preOrderInfo.value = data
}
// 省略前面小节的代码...
// 生成预付订单
createPreOrder()
</script>
<template>
<scroll-page background-color="#f6f6f6">
<view class="medicine-page">
<!-- 省略前面小节的代码... -->
<!-- 药品列表 -->
<view class="medicine-list">
<view
v-for="medicine in preOrderInfo.medicines"
:key="medicine.id"
class="medicine-list-item"
>
<image
class="medicine-cover"
:src="medicine.avatar"
mode="aspectFill"
/>
<view class="medicine-info">
<text class="name">{{ medicine.name }}</text>
<text
:class="{ symbol: medicine.prescriptionFlag === 1 }"
class="symbol"
>
{{ medicine.specs }}
</text>
<text class="price">¥{{ medicine.amount }}</text>
</view>
<view class="quantity">x{{ medicine.quantity }}</view>
<view class="guide">用法用量:{{ medicine.usageDosag }}</view>
</view>
</view>
<!-- 订单信息 -->
<view class="order-info">
<uni-list :border="false">
<uni-list-item
title="药品金额"
:right-text="'¥' + preOrderInfo.payment"
/>
<uni-list-item
title="运费"
:right-text="'¥' + preOrderInfo.expressFee"
/>
<uni-list-item
title="优惠券"
show-arrow
:right-text="'-¥' + preOrderInfo.couponDeduction"
/>
<uni-list-item
title="实付款"
:right-text="'¥' + preOrderInfo.actualPayment"
/>
</uni-list>
</view>
<!-- 底部 -->
<view class="toolbar">
<view class="total-amount">
需付款: <text class="number">¥{{ preOrderInfo.actualPayment }}</text>
</view>
<view class="buttons">
<button class="uni-button">立即支付</button>
</view>
</view>
</view>
</scroll-page>
</template>
1.2 待支付订单
1.2.1 支付渠道
将支付渠道组件 custom-payment 应用到页面中,组件需要处理以下部分的逻辑:
- 定义组件 ref 属性,获取组件实例
- 传入药品订单要支付的金额
- 传入药品订单的 ID
- 监听组件
confirm事件和close事件
<!-- subpkg_medicine/payment/index.vue -->
<script setup>
import { ref } from 'vue'
import { preOrderApi } from '@/services/medicine'
import { addressListApi } from '@/services/address'
// 省略前面小节的代码...
// 待支付订单ID
const orderId = ref('')
// 支付渠道组件实例
const paymentRef = ref()
// 关闭支付渠道弹层
function onPaymentClose() {}
// 支付渠道确认支付
function onPaymentConfirm() {}
// 选择支付渠道
function onPaymentButtonClick() {
paymentRef.value.open()
}
// 省略前面小节的代码...
</script>
<template>
<scroll-page background-color="#f6f6f6">
...
</scroll-page>
<!-- 支付渠道 -->
<custom-payment
@close="onPaymentClose"
@confirm="onPaymentConfirm"
:amount="preOrderInfo.actualPayment"
:order-id="orderId"
ref="paymentRef"
/>
</template>
1.2.2 待付订单ID
调用接口创建待付订单,接口文档的地址在这里,按以下步骤来实现:
- 封装调用接口的方法
// services/medicine.js
import { http } from '@/utils/http.js'
// 省略前面小节的代码...
/**
* 药品待支付订单
*/
export const createOrderApi = (id, addressId) => {
return http.post('/patient/medicine/order', { id, addressId })
}
- 在页面中调用方法生成待支付订单
<!-- subpkg_medicine/payment/index.vue -->
<script setup>
import { ref } from 'vue'
import { createOrderApi, preOrderApi } from '@/services/medicine'
import { addressListApi } from '@/services/address'
// 省略前面小节的代码...
// 待支付订单ID
const orderId = ref('')
// 省略前面小节的代码...
// 选择支付渠道
async function onPaymentButtonClick() {
// 生成待支付订单接口
const { code, data, message } = await createOrderApi(
props.id,
addressInfo.value.id
)
// 检测接口是否调用成功
if (code !== 10000) return uni.utils.toast(message)
// 传递订单ID
orderId.value = data.id
// 打开支付渠道
paymentRef.value.open()
}
// 省略前面小节的代码...
</script>
1.2.3 支付宝支付
支付宝支付的 API 在问诊订单支付时已经封装好了,在这里直接调用就可以了
<!-- subpkg_medicine/payment/index.vue -->
<script setup>
import { ref } from 'vue'
import { createOrderApi, preOrderApi } from '@/services/medicine'
import { addressListApi } from '@/services/address'
import { paymentApi } from '@/services/payment'
// 省略前面小节的代码...
// 支付渠道确认支付
async function onPaymentConfirm({ index }) {
if (index === 0) return uni.utils.toast('暂不支持微信支付!')
// 调用后端提供的支付接口
const { code, data, message } = await paymentApi({
orderId: orderId.value,
paymentMethod: index,
payCallback: 'http://localhost:5173/#/subpkg_medicine/pay_result/index',
})
// 接口是否调用成功
if (code !== 10000) return uni.utils.toast(message)
// 支付宝支付页面
window.location.href = data.payUrl
}
// 省略前面小节的代码...
</script>
在上述的代码中要注意支付成功后的回跳地址是 /subpkg_medicine/pay_result/index,以下是这个页面的布局模板代码:
<!-- subpkg_medicine/pay_result/index.vue -->
<script setup></script>
<template>
<view class="pay-result-page">
<view class="result">
<uni-icons size="70" color="#20c6b2" type="checkbox-filled" />
<view class="amount">¥ 35.00</view>
<view class="label">支付成功</view>
<view class="tips">订单支付成功,已通知药房尽快发出,请耐心等待~</view>
</view>
<view class="buttons">
<navigator
hover-class="none"
url="/subpkg_medicine/order_detail/index"
class="uni-button"
>
查看订单
</navigator>
<navigator
hover-class="none"
url="/subpkg_consult/room/index"
open-type="redirect"
class="uni-button plain"
>
返回诊室
</navigator>
</view>
</view>
</template>
<style lang="scss">
@import './index.scss';
</style>
// subpkg_medicine/pay_result/index.scss
.pay-result-page {
padding: 100rpx 60rpx;
}
.result {
line-height: 1;
text-align: center;
.amount {
font-size: 44rpx;
font-weight: 600;
color: #333;
margin-top: 30rpx;
}
.label {
font-size: 28rpx;
color: #3c3e42;
margin-top: 20rpx;
}
.tips {
line-height: 1.5;
padding: 0 70rpx;
font-size: 28rpx;
color: #979797;
margin-top: 40rpx;
}
}
.buttons {
display: flex;
justify-content: space-between;
margin-top: 60rpx;
}
.uni-button {
width: 280rpx;
&.plain {
background-color: #fff;
color: #20c6b2;
border: 1px solid #20c6b2;
}
}
1.2.4 支付结果
在支付结果页面除了展示支付的金额外还允许查看订单详情和返回问诊室,按以下步骤来实现:
- 封装调用药品订单详情接口的方法
// services/medicine.js
- 在页面中调用接口并渲染获取的数据
<!-- subpkg_medicine/pay_result/index.vue -->
<script setup>
import { ref } from 'vue'
import { orderDetailApi } from '@/services/medicine'
// 获取地址的参数
const props = defineProps({
orderId: String,
})
// 药品订单详情
const orderDetail = ref({})
// 查询订单详情
async function getOrderDetail(orderId) {
// 订单详情接口
const { code, data, message } = await orderDetailApi(props.orderId)
// 检测接口是否调用成功
if (code !== 10000) return uni.utils.toast(message)
// 渲染订单数据
orderDetail.value = data
}
// 查询药品订单详情
getOrderDetail()
</script>
<template>
<view class="pay-result-page">
<view class="result">
<uni-icons size="70" color="#20c6b2" type="checkbox-filled" />
<view class="amount">¥ {{ orderDetail.actualPayment }}</view>
<view class="label">支付成功</view>
<view class="tips">订单支付成功,已通知药房尽快发出,请耐心等待~</view>
</view>
<view class="buttons">
<navigator
hover-class="none"
:url="`/subpkg_medicine/order_detail/index?id=${orderDetail.id}`"
class="uni-button"
>
查看订单
</navigator>
<navigator
hover-class="none"
:url="`/subpkg_consult/room/index?orderId=${orderDetail.roomId}`"
open-type="redirect"
class="uni-button plain"
>
返回诊室
</navigator>
</view>
</view>
</template>
注意事项:
- 在详情数据中
id属性对应的是药品订单ID - 在详情数据中
roomId属性对应的是问诊室的ID
1.3 订单详情
根据药品订单ID查看药品订单的详情。
1.3.1 页面模板
<!-- subpkg_medicine/order_detail/index.vue -->
<script setup></script>
<template>
<scroll-page background-color="#f6f6f6">
<view class="medicine-page">
<view class="page-header">
<view class="order-status">
<text class="label">药品订单 45元</text>
<text class="status">待支付</text>
</view>
<view class="order-shippment">
<template v-if="true">
<view class="region">
<text class="iconfont icon-location"></text>
广东省广州市
</view>
<view class="detail">大华区明离路科技园880号</view>
<view class="receiver">李富贵 1885138766</view>
</template>
<template v-else>
<navigator hover-class="none" url=" ">
<view class="marker">【东莞市】您的包裹已签收</view>
<view class="datetime">2019-07-14 17:42:12</view>
<view class="arrow">
<uni-icons size="18" color="#C3C3C5" type="forward" />
</view>
</navigator>
</template>
</view>
</view>
<view class="order-shop">
优医药房
<text class="alt">优医质保 假一赔十</text>
</view>
<!-- 药品列表 -->
<view class="medicine-list">
<view class="medicine-list-item">
<image
class="medicine-cover"
src="/static/uploads/medicine-1.jpg"
mode="aspectFill"
/>
<view class="medicine-info">
<text class="name">瑞巴派特片</text>
<text class="unit symbol">24片</text>
<text class="price">¥25.00</text>
</view>
<view class="quantity">x1</view>
<view class="guide">用法用量:口服,每次1袋,每天3次,用药3天</view>
</view>
<view class="medicine-list-item">
<image
class="medicine-cover"
src="/static/uploads/medicine-2.jpg"
mode="aspectFill"
/>
<view class="medicine-info">
<text class="name">瑞巴派特片</text>
<text class="unit symbol">24片</text>
<text class="price">¥25.00</text>
</view>
<view class="quantity">x1</view>
<view class="guide">用法用量:口服,每次1袋,每天3次,用药3天</view>
</view>
</view>
<!-- 订单信息 -->
<view class="order-info">
<uni-list :border="false">
<uni-list-item title="药品金额" right-text="¥50.00" />
<uni-list-item title="运费" right-text="¥5.00" />
<uni-list-item title="优惠券" show-arrow right-text="-¥10.00" />
<uni-list-item title="实付款" right-text="¥45.00" />
<uni-list-item title="订单编号" right-text="202201127465" />
<uni-list-item title="创建时间" right-text="2022-01-23 09:23:46" />
<uni-list-item title="支付方式" right-text="微信支付" />
</uni-list>
</view>
<view class="notice-bar">
<uni-notice-bar text="请在 00:14:59 内完成支付,超时订单将取消" />
</view>
<!-- 底部 -->
<view class="toolbar">
<template v-if="false">
<view class="total-amount">
需付款: <text class="number">¥39.00</text>
</view>
<view class="buttons">
<button class="uni-button minor">取消问诊</button>
<button class="uni-button">立即支付</button>
</view>
</template>
<template v-else>
<view class="buttons">
<view class="delete-botton">
<uni-icons size="24" color="#848484" type="trash" />
<text>删除订单</text>
</view>
<button class="uni-button">再次购买</button>
</view>
</template>
</view>
</view>
</scroll-page>
</template>
<style lang="scss">
@import './index.scss';
</style>
// subpkg_medicine/order_detail/index.scss
.medicine-page {
padding-bottom: calc(env(safe-area-inset-bottom) + 155rpx);
}
.page-header {
padding: 20rpx 30rpx 30rpx;
// 需要一个高为300的背景图
background-color: #eaf8f6;
overflow: hidden;
.order-status {
display: flex;
justify-content: space-between;
line-height: 1;
padding: 30rpx 0 0 6rpx;
font-size: 32rpx;
.label {
color: #121826;
}
.status {
color: #16c2a3;
}
}
.order-shippment {
line-height: 1;
padding: 30rpx;
background-color: #fff;
border-radius: 20rpx;
margin-top: 40rpx;
position: relative;
.region {
display: flex;
align-items: center;
font-size: 26rpx;
color: #6f6f6f;
}
.icon-location {
color: #ff7702;
font-size: 28rpx;
margin-right: 5rpx;
}
.detail {
margin: 24rpx 0;
font-size: 34rpx;
color: #121826;
}
.receiver {
font-size: 26rpx;
color: #121826;
}
.marker {
font-size: 28rpx;
color: #16c2a3;
}
.datetime {
// margin-left: 10rpx;
margin-top: 20rpx;
font-size: 24rpx;
color: #c3c3c5;
}
.arrow {
position: absolute;
top: 50%;
right: 16rpx;
transform: translateY(-50%);
}
}
}
.order-shop {
padding: 20rpx 30rpx;
margin: 10rpx 0;
line-height: 1;
font-size: 32rpx;
color: #121826;
font-weight: 500;
.alt {
font-size: 24rpx;
color: #979797;
margin-left: 10rpx;
}
}
.medicine-list {
padding: 0 30rpx;
background-color: #fff;
}
.medicine-list-item {
padding: 30rpx 0;
border-bottom: 1rpx solid #ededed;
position: relative;
&:last-child {
border-bottom: none;
}
.medicine-cover {
width: 180rpx;
height: 160rpx;
float: left;
}
.medicine-info {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 160rpx;
padding-left: 20rpx;
.name {
font-size: 30rpx;
font-weight: 500;
color: #121826;
}
.unit {
color: #979797;
font-size: 28rpx;
display: flex;
align-items: center;
@include text-overflow(1);
&.symbol::before {
content: '处方药';
line-height: 1;
padding: 8rpx 10rpx;
margin-right: 10rpx;
border-radius: 4rpx;
font-size: 24rpx;
color: #fff;
background-color: #16c2a3;
}
}
.price {
color: #eb5757;
font-size: 32rpx;
}
}
.quantity {
top: 30rpx;
right: 0;
position: absolute;
font-size: 30rpx;
color: #121826;
}
.guide {
clear: both;
padding: 20rpx;
margin-top: 30rpx;
font-size: 28rpx;
color: #848484;
border-radius: 10rpx;
background-color: #f6f6f6;
}
}
.order-info {
padding: 0 30rpx;
margin-top: 30rpx;
background-color: #fff;
.right-text {
display: flex;
align-items: center;
}
.copy {
line-height: 1;
padding: 6rpx 20rpx 4rpx;
margin-right: 20rpx;
font-size: 24rpx;
color: #16c2a3;
background-color: #fafafa;
border-radius: 40rpx;
border: 1rpx solid #16c2a3;
}
.number {
font-size: 32rpx;
color: #848484;
}
:deep(.uni-list-item__container) {
padding-left: 0 !important;
padding-right: 0 !important;
}
:deep(.uni-list-item__content-title) {
font-size: 32rpx !important;
color: #3c3e42 !important;
}
:deep(.uni-list-item__extra-text) {
font-size: 32rpx !important;
color: #848484 !important;
}
:deep(.uni-icon-wrapper) {
padding: 0 !important;
margin-right: -10rpx !important;
font-size: 36rpx !important;
margin-top: 2rpx;
}
}
.notice-bar {
text-align: center;
margin-top: 30rpx;
position: sticky;
bottom: calc(env(safe-area-inset-bottom) + 145rpx);
}
.toolbar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
height: 88rpx;
padding: 30rpx 40rpx calc(env(safe-area-inset-bottom) + 30rpx);
background-color: #fff;
.buttons {
flex: 1;
display: flex;
}
.delete-botton {
display: flex;
flex-direction: column;
color: #848484;
text-align: center;
font-size: 24rpx;
margin: 0 60rpx 0 30rpx;
}
.uni-button {
flex: 1;
padding: 0 30rpx;
&.minor {
color: #16c2a3;
margin-right: 20rpx;
border: 1rpx solid #ededed;
background-color: #fafafa;
}
}
.total-amount {
display: flex;
align-items: center;
width: 270rpx;
font-size: 28rpx;
color: #3c3e42;
}
.number {
font-size: 40rpx;
color: #eb5757;
margin-left: 10rpx;
}
}
1.3.2 调用接口
药品订单详情的接口在前面小节中已经封装完毕
<!-- subpkg_medicine/order_detail/index.vue -->
<script setup>
import { ref } from 'vue'
import { orderDetailApi } from '@/services/medicine'
// 接收地址的ID
const props = defineProps({
id: String,
})
// 药口订单详情
const orderDetail = ref({})
// 药品订单详情
async function getOrderDetail() {
// 订单详情接口
const { code, data, message } = await orderDetailApi(props.id)
// 检测接口是否调用成功
if (code !== 10000) return uni.utils.toast(message)
// 渲染订单详情数据
orderDetail.value = data
}
// 获取药品订单详情
getOrderDetail()
</script>
1.3.3 渲染数据
页面数据较多,分部分进行渲染
- 订单的状态信息
<!-- subpkg_medicine/order_detail/index.vue -->
<script setup>
import { ref } from 'vue'
import { orderDetailApi } from '@/services/medicine'
// 省略前面小节的代码...
// 订单状态
const orderStatus = ref({
10: '待支付',
11: '待发货',
12: '待收货',
13: '已完成',
14: '已取消',
})
// 省略前面小节的代码...
</script>
<template>
<scroll-page background-color="#f6f6f6">
<view class="medicine-page">
<view class="page-header">
<view class="order-status">
<text class="label">药品订单 45元</text>
<text class="status">待支付</text>
</view>
<!-- 省略前面小节的代码... -->
</view>
<!-- 省略前面小节的代码... -->
</view>
</scroll-page>
</template>
- 收货地址信息,根据订单状态展示收货地址信息
status值为 11 时表示待发货status值为 12 时表示待收货
<!-- subpkg_medicine/order_detail/index.vue -->
<template>
<scroll-page background-color="#f6f6f6">
<view class="medicine-page">
<view class="page-header">
<!-- 省略前面小节的代码... -->
<view class="order-shippment">
<template v-if="orderDetail.status === 11">
<view class="region">
<text class="iconfont icon-location"></text>
{{ orderDetail.addressInfo.province }}
{{ orderDetail.addressInfo.city }}
{{ orderDetail.addressInfo.county }}
</view>
<view class="detail">
{{ orderDetail.addressInfo.addressDetail }}
</view>
<view class="receiver">
{{ orderDetail.addressInfo.receiver }}
{{ orderDetail.addressInfo.mobile }}
</view>
</template>
<template v-if="orderDetail.status === 12">
<navigator hover-class="none" url=" ">
<view class="marker">{{ orderDetail.expressInfo.content }}</view>
<view class="datetime">{{ orderDetail.expressInfo.time }}</view>
<view class="arrow">
<uni-icons size="18" color="#C3C3C5" type="forward" />
</view>
</navigator>
</template>
</view>
</view>
<!-- 省略前面小节的代码... -->
</view>
</scroll-page>
</template>
- 药品列表
<!-- subpkg_medicine/order_detail/index.vue -->
<template>
<scroll-page background-color="#f6f6f6">
<view class="medicine-page">
<!-- 省略前面小节的代码... -->
<!-- 药品列表 -->
<view class="medicine-list">
<view
v-for="medicine in orderDetail.medicines"
:key="medicine.id"
class="medicine-list-item"
>
<image
class="medicine-cover"
:src="medicine.avatar"
mode="aspectFill"
/>
<view class="medicine-info">
<text class="name">{{ medicine.name }}</text>
<text class="unit symbol">24片</text>
<text class="price">¥{{ medicine.amount }}</text>
</view>
<view class="quantity">x{{ medicine.quantity }}</view>
<view class="guide">用法用量:{{ medicine.usageDosag }}</view>
</view>
</view>
<!-- 省略前面小节的代码... -->
</view>
</scroll-page>
</template>
- 订单信息
<!-- subpkg_medicine/order_detail/index.vue -->
<script setup>
// 省略前面小节的代码...
// 支付方式
const paymentMethods = ['微信支付', '支付宝支付']
// 省略前面小节的代码...
</script>
<template>
<scroll-page background-color="#f6f6f6">
<view class="medicine-page">
<!-- 省略前面小节的代码... -->
<!-- 订单信息 -->
<view class="order-info">
<uni-list :border="false">
<uni-list-item
title="药品金额"
:right-text="'¥' + orderDetail.payment"
/>
<uni-list-item
title="运费"
:right-text="'¥' + orderDetail.expressFee"
/>
<uni-list-item
title="优惠券"
show-arrow
:right-text="'-¥' + orderDetail.couponDeduction"
/>
<uni-list-item
title="实付款"
:right-text="'¥' + orderDetail.actualPayment"
/>
<uni-list-item title="订单编号" :right-text="orderDetail.orderNo" />
<uni-list-item
title="创建时间"
:right-text="orderDetail.createTime"
/>
<uni-list-item
title="支付方式"
:right-text="paymentMethods[orderDetail.paymentMethod]"
/>
</uni-list>
</view>
<!-- 省略前面小节的代码... -->
</view>
</scroll-page>
</template>
1.4 订单列表(作业)
在首页面或我的页面可以进入药品订单列表页面。
1.4.1 页面模板
<!-- subpkg_medicine/order_list/index.vue -->
<script setup>
import { ref } from 'vue'
const feedTabs = ref([
{ label: '全部', rendered: true },
{ label: '待支付', rendered: false },
{ label: '待发货', rendered: false },
{ label: '待收货', rendered: false },
{ label: '已完成', rendered: false },
])
</script>
<template>
<view class="medicine-page">
<view class="order-status-tabs">
<custom-tabs :list="feedTabs"></custom-tabs>
</view>
<scroll-view
refresher-enabled
refresher-background="#f6f6f6"
class="uni-scroll-view"
scroll-y
>
<view class="order-list">
<view class="order-list-item">
<view class="order-header">
<text class="number">订单编号: 202201127465</text>
<text class="status">待支付</text>
</view>
<navigator
url="/subpkg_medicine/order_detail/index"
hover-class="none"
class="order-body"
>
<view class="medicine">
<image
class="medicine-cover"
src="/static/uploads/medicine-1.jpg"
mode="aspectFill"
/>
<view class="medicine-info">
<text class="name">瑞巴派特片</text>
<text class="unit symbol">24片</text>
<text class="price">¥25.00</text>
</view>
<view class="quantity">x1</view>
</view>
<view class="medicine">
<image
class="medicine-cover"
src="/static/uploads/medicine-2.jpg"
mode="aspectFill"
/>
<view class="medicine-info">
<text class="name">瑞巴派特片</text>
<text class="unit symbol">24片</text>
<text class="price">¥25.00</text>
</view>
<view class="quantity">x1</view>
</view>
</navigator>
<view class="order-footer">
<view class="total">共2件商品 合计:¥49.00 (运费¥0.00)</view>
<view class="buttons">
<button class="uni-button minor">取消订单</button>
<button class="uni-button">继续支付</button>
</view>
</view>
</view>
<!-- 加载状态 -->
<uni-load-more
status="loading"
color="#C3C3C5"
:icon-size="16"
:content-text="{
contentdown: '上拉显示更多',
contentrefresh: '数据正在加载中',
contentnomore: '没有更多数据了',
}"
/>
</view>
</scroll-view>
</view>
</template>
<style lang="scss">
@import './index.scss';
</style>
// subpkg_medicine/order_list/index.scss
.medicine-page {
height: 100vh;
/* #ifdef H5 */
height: calc(100vh - 44px);
/* #endif */
background-color: #f6f6f6;
box-sizing: border-box;
padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx);
display: flex;
flex-direction: column;
}
.order-status-tabs {
padding: 20rpx 30rpx 0;
background-color: #fff;
:deep(.custom-tabs) {
justify-content: space-evenly;
}
}
.uni-scroll-view {
flex: 1;
overflow: hidden;
padding-top: 15rpx;
}
.order-list {
padding: 15rpx 30rpx 30rpx;
}
.order-list-item {
// height: 715rpx;
padding: 0 30rpx;
margin-bottom: 30rpx;
border-radius: 10rpx;
background-color: #fff;
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
height: 100rpx;
padding: 0 30rpx;
margin: 0 -30rpx;
font-size: 26rpx;
border-bottom: 1rpx solid #f5f5f5;
.number {
color: #848484;
}
.status {
color: #f2994a;
}
}
.medicine {
padding: 30rpx 0;
border-bottom: 1rpx solid #f5f5f5;
position: relative;
.medicine-cover {
width: 180rpx;
height: 160rpx;
float: left;
}
.medicine-info {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 160rpx;
padding-left: 20rpx;
.name {
font-size: 30rpx;
font-weight: 500;
color: #121826;
}
.unit {
color: #979797;
font-size: 28rpx;
display: flex;
align-items: center;
@include text-overflow(1);
&.symbol::before {
content: '处方药';
line-height: 1;
padding: 8rpx 10rpx;
margin-right: 10rpx;
border-radius: 4rpx;
font-size: 24rpx;
color: #fff;
background-color: #16c2a3;
}
}
.price {
color: #eb5757;
font-size: 32rpx;
}
}
.quantity {
top: 30rpx;
right: 0;
position: absolute;
font-size: 30rpx;
color: #121826;
}
}
.order-footer {
padding: 30rpx 0;
.total {
text-align: right;
font-size: 24rpx;
color: #3c3e42;
}
.buttons {
display: flex;
justify-content: flex-end;
padding-top: 20rpx;
}
.uni-button {
height: 60rpx;
line-height: 60rpx;
margin: 20rpx 0 0 30rpx;
font-size: 26rpx;
color: #16c2a3;
border: 1rpx solid #16c2a3;
background-color: #fff;
&.minor {
color: #3c3e42;
border-color: #ededed;
background-color: #f6f6f6;
}
}
}
}
1.4.2 调用接口
- 封装调用接口的方法,接口文档地址在这里
| 订单状态 | 说明 | 备注 |
|---|---|---|
| 10 | 待支付 | |
| 11 | 待发货 | |
| 12 | 待收货 | |
| 13 | 已完成 | |
| 14 | 已取消 |
// services/medicine.js
import { http } from '@/utils/http.js'
// 省略前面小节的代码...
/**
* 药品订单列表
*/
export const orderListApi = (status = 10, current = 1, pageSize = 10) => {
return http.get('/patient/medicine/order/mylist', {
params: { status, current, pageSize },
})
}
- 调用接口获取数据
<!-- subpkg_medicine/order_list/index.vue -->
<script setup>
import { ref } from 'vue'
import { orderListApi } from '@/services/medicine'
// 标签页数据
const feedTabs = ref([
{ label: '待支付', status: 10, rendered: false },
{ label: '待发货', status: 11, rendered: false },
{ label: '待收货', status: 12, rendered: false },
{ label: '已完成', status: 13, rendered: false },
{ label: '已取消', status: 14, rendered: true },
])
// 订单列表
const orderList = ref([])
// 标签页切换
function onTabClick({ index, status }) {
// 获取订单列表
getOrderList(status)
}
// 药品订单列表
async function getOrderList(status) {
// 订单列表接口
const { code, data, message } = await orderListApi(status)
// 检测接口是否调用成功
if (code !== 10000) return uni.utils.toast(message)
// 渲染列表数据
orderList.value = data.rows
}
// 获取药品订单列表
getOrderList()
</script>
<template>
<view class="medicine-page">
<view class="order-status-tabs">
<custom-tabs @click="onTabClick" :list="feedTabs"></custom-tabs>
</view>
<scroll-view
refresher-enabled
refresher-background="#f6f6f6"
class="uni-scroll-view"
scroll-y
>
...
</scroll-view>
</view>
</template>
1.4.3 渲染数据
<!-- subpkg_medicine/order_list/index.vue -->
<script setup>
import { ref } from 'vue'
// 省略前面小节的代码...
// 加载状态
const loading = ref(true)
// 列表是否为空
const isEmpty = ref(false)
// 省略前面小节的代码...
// 药品订单列表
async function getOrderList(status) {
// 显示加载状态
loading.value = true
// 列表是否为空
isEmpty.value = false
// 订单列表接口
const { code, data, message } = await orderListApi(status)
// 检测接口是否调用成功
if (code !== 10000) return uni.utils.toast(message)
// 渲染列表数据
orderList.value = data.rows
// 关闭加载状态
loading.value = false
// 列表是是否为空
isEmpty.value = orderList.value.length === 0
}
// 省略前面小节的代码...
</script>
<template>
<view class="medicine-page">
<view class="order-status-tabs">
<custom-tabs @click="onTabClick" :list="feedTabs"></custom-tabs>
</view>
<scroll-view
refresher-enabled
refresher-background="#f6f6f6"
class="uni-scroll-view"
scroll-y
>
<view class="order-list">
<view
v-for="order in orderList"
:key="order.id"
class="order-list-item"
>
<view class="order-header">
<text class="number">订单编号: {{ order.id }}</text>
<text class="status">待支付</text>
</view>
<navigator
:url="`/subpkg_medicine/order_detail/index?id=${order.id}`"
hover-class="none"
class="order-body"
>
<view
v-for="medicine in order.medicines"
:key="medicine.id"
class="medicine"
>
<image
class="medicine-cover"
:src="medicine.avatar"
mode="aspectFill"
/>
<view class="medicine-info">
<text class="name">{{ medicine.name }}</text>
<text
:class="{ symbol: medicine.prescriptionFlag === 1 }"
class="unit"
>
{{ medicine.specs }}
</text>
<text class="price">¥{{ medicine.amount }}</text>
</view>
<view class="quantity">x{{ medicine.quantity }}</view>
</view>
</navigator>
<view class="order-footer">
<view class="total">
共2件商品 合计:¥{{ order.actualPayment }}(运费¥0.00)
</view>
<view class="buttons">
<button class="uni-button minor">取消订单</button>
<button class="uni-button">继续支付</button>
</view>
</view>
</view>
<!-- 列表数据为空 -->
<view v-if="isEmpty" class="empty-tips">没有更多数据了</view>
<!-- 加载状态 -->
<uni-load-more
v-show="loading"
status="loading"
color="#C3C3C5"
:icon-size="16"
:content-text="{
contentdown: '上拉显示更多',
contentrefresh: '数据正在加载中',
contentnomore: '没有更多数据了',
}"
/>
</view>
</scroll-view>
</view>
</template>
在 App.vue 中添加空列表提示的样式
<style lang="scss">
// 省略前面小节的代码...
.empty-tips {
margin-top: 40rpx;
text-align: center;
font-size: 28rpx;
color: #c3c3c5;
}
</style>
二、问诊订单
在 subpkg_consult 分包下增加两个页面,先去创建页面再添加下面的配置代码
{
"subPackages": [
{
"root": "subpkg_consult",
"pages": [
{...},
{
"path": "order_list/index",
"style": {
"navigationBarTitleText": "订单列表"
}
},
{
"path": "order_detail/index",
"style": {
"navigationBarTitleText": "订单详情"
}
}
]
}
]
}
2.1 订单列表
2.1.1 页面模板
<!-- subpkg_consult/order_list/components/order-list.vue -->
<script setup></script>
<template>
<scroll-view
refresher-enabled
refresher-background="#f6f6f6"
class="uni-scroll-view"
scroll-y
>
<view class="consult-list">
<view class="consult-list-item">
<view class="consult-header">
<view class="label">
<image
class="doctor-avatar"
src="/static/uploads/doctor-avatar.jpg"
/>
<text>极速问诊(自动分配医生)</text>
</view>
<text class="status color-1">咨询中</text>
</view>
<navigator
class="consult-body"
hover-class="none"
url="/subpkg_consult/order_detail/index"
>
<uni-list :border="false">
<uni-list-item
:border="false"
title="病情描述"
right-text="腹痛腹泻 胃部有些痉挛"
/>
<uni-list-item :border="false" title="价格" right-text="¥ 39.00" />
<uni-list-item
:border="false"
title="创建时间"
right-text="2019-07-08 09:55:54"
/>
</uni-list>
</navigator>
<view class="consult-footer">
<button class="uni-button minor">取消订单</button>
<button class="uni-button">继续支付</button>
</view>
</view>
<view class="consult-list-item">
<view class="consult-header">
<view class="label">
<image
class="doctor-avatar"
src="/static/uploads/doctor-avatar.jpg"
/>
<text>极速问诊(自动分配医生)</text>
</view>
<text class="status color-3">待支付</text>
</view>
<navigator
class="consult-body"
hover-class="none"
url="/subpkg_consult/order_detail/index"
>
<uni-list :border="false">
<uni-list-item
:border="false"
title="病情描述"
right-text="腹痛腹泻 胃部有些痉挛"
/>
<uni-list-item :border="false" title="价格" right-text="¥ 39.00" />
<uni-list-item
:border="false"
title="创建时间"
right-text="2019-07-08 09:55:54"
/>
</uni-list>
</navigator>
<view class="consult-footer">
<view class="uni-button more">
<text class="label">更多</text>
<uni-transition :show="true">
<view class="list">
<view class="list-item">查看处方</view>
<view class="list-item">删除订单</view>
</view>
</uni-transition>
</view>
<button class="uni-button minor">取消订单</button>
<button class="uni-button">继续支付</button>
</view>
</view>
<view class="consult-list-item">
<view class="consult-header">
<view class="label">
<image
class="doctor-avatar"
src="/static/uploads/doctor-avatar.jpg"
/>
<text>极速问诊(自动分配医生)</text>
</view>
<text class="status">待接诊</text>
</view>
<navigator
class="consult-body"
hover-class="none"
url="/subpkg_consult/order_detail/index"
>
<uni-list :border="false">
<uni-list-item
:border="false"
title="病情描述"
right-text="腹痛腹泻 胃部有些痉挛"
/>
<uni-list-item :border="false" title="价格" right-text="¥ 39.00" />
<uni-list-item
:border="false"
title="创建时间"
right-text="2019-07-08 09:55:54"
/>
</uni-list>
</navigator>
<view class="consult-footer">
<button class="uni-button minor">取消订单</button>
<button class="uni-button">继续支付</button>
</view>
</view>
<view class="consult-list-item">
<view class="consult-header">
<view class="label">
<image
class="doctor-avatar"
src="/static/uploads/doctor-avatar.jpg"
/>
<text>极速问诊(自动分配医生)</text>
</view>
<text class="status color-2">已取消</text>
</view>
<navigator
class="consult-body"
hover-class="none"
url="/subpkg_consult/order_detail/index"
>
<uni-list :border="false">
<uni-list-item
:border="false"
title="病情描述"
right-text="腹痛腹泻 胃部有些痉挛"
/>
<uni-list-item :border="false" title="价格" right-text="¥ 39.00" />
<uni-list-item
:border="false"
title="创建时间"
right-text="2019-07-08 09:55:54"
/>
</uni-list>
</navigator>
<view class="consult-footer">
<button class="uni-button minor">取消订单</button>
<button class="uni-button">继续支付</button>
</view>
</view>
<!-- 加载状态 -->
<uni-load-more
status="loading"
color="#C3C3C5"
:icon-size="16"
:content-text="{
contentdown: '上拉显示更多',
contentrefresh: '数据正在加载中',
contentnomore: '没有更多数据了',
}"
/>
</view>
</scroll-view>
</template>
<style lang="scss">
@import './styles.scss';
</style>
// subpkg_consult/order_list/components/styles.scss
.uni-scroll-view {
height: calc(100vh - 50px);
/* #ifdef H5 */
height: calc(100vh - 94px);
/* #endif */
overflow: hidden;
box-sizing: border-box;
padding-top: 15rpx;
padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx);
}
.consult-list {
padding: 15rpx 30rpx 30rpx;
}
.consult-list-item {
// height: 715rpx;
padding: 0 30rpx;
margin-bottom: 30rpx;
border-radius: 10rpx;
background-color: #fff;
.consult-header {
display: flex;
justify-content: space-between;
align-items: center;
height: 100rpx;
padding: 0 30rpx;
margin: 0 -30rpx;
font-size: 30rpx;
border-bottom: 1rpx solid #f5f5f5;
.label {
display: flex;
align-items: center;
color: #000;
font-weight: 500;
}
.doctor-avatar {
width: 40rpx;
height: 40rpx;
margin-right: 10rpx;
border-radius: 50%;
}
.status {
color: #121826;
}
.color-1 {
color: #f2994a;
}
.color-2 {
color: #16c2a3;
}
.color-3 {
color: #848484;
}
}
.consult-body {
padding: 20rpx 0;
}
:deep(.uni-list-item__container) {
padding: 10rpx 0 !important;
}
:deep(.uni-list-item__content-title) {
font-size: 28rpx !important;
color: #c3c3c5 !important;
}
:deep(.uni-list-item__extra-text) {
font-size: 28rpx !important;
color: #333 !important;
}
:deep(.uni-list-item__extra) {
width: 480rpx !important;
justify-content: flex-start !important;
}
.consult-footer {
display: flex;
justify-content: flex-end;
padding: 30rpx 0;
border-top: 1rpx solid #f5f5f5;
.uni-button {
height: 60rpx;
line-height: 60rpx;
/* #ifdef MP */
line-height: 59rpx;
/* #endif */
margin: 0 0 0 30rpx;
font-size: 26rpx;
color: #16c2a3;
border: 1rpx solid #16c2a3;
background-color: #fff;
&.more {
flex: 1;
text-align: left;
margin-left: 0;
border-color: transparent;
color: #979797;
position: relative;
}
.list {
position: absolute;
left: 0;
bottom: 75rpx;
z-index: 99;
width: 160rpx;
text-align: center;
padding: 10rpx 30rpx;
border-radius: 10rpx;
font-size: 28rpx;
box-shadow: 0 0 30rpx rgba(50, 50, 51, 0.3);
background-color: #fff;
&::after {
position: absolute;
bottom: -28rpx;
left: 30rpx;
content: '';
border-width: 14rpx;
border-style: solid;
border-color: #fff transparent transparent transparent;
}
}
.list-item {
padding: 10rpx 0;
border-bottom: 1rpx solid #eee;
&:last-child {
border-bottom: none;
}
}
&.minor {
color: #3c3e42;
border-color: #ededed;
background-color: #fafafa;
}
}
}
}
<!-- subpkg_consult/order_list/index.vue -->
<script setup>
import { ref } from 'vue'
// 订单列表组件
import orderList from './components/order-list.vue'
// 标签索引值
const tabIndex = ref(0)
// 标签页数据
const orderTabs = reactive([
{ label: '问医生', type: 1, rendered: true },
{ label: '极速问诊', type: 2, rendered: false },
{ label: '开药问诊', type: 3, rendered: false },
])
</script>
<template>
<view class="consult-page">
<view class="consult-status-tabs">
<custom-tabs :list="orderTabs"></custom-tabs>
</view>
<!-- 订单列表 -->
<order-list />
</view>
</template>
<style lang="scss">
@import './index.scss';
</style>
// subpkg_consult/order_list/index.scss
.consult-page {
background-color: #f6f6f6;
}
.consult-status-tabs {
padding: 20rpx 30rpx 0;
background-color: #fff;
:deep(.custom-tabs) {
justify-content: space-evenly;
}
}
2.1.2 组件交互
切换标签页时,每个标签页对应了一个数据列表,每个列表只被初始化一次
为此我们要构造一个数据结构:
<!-- subpkg_consult/order_list/index.vue -->
<script setup>
import { ref } from 'vue'
// 省略前面小节的代码...
// 标签索引值
const tabIndex = ref(0)
// 标签页数据
const orderTabs = reactive([
{ label: '问医生', type: 1, rendered: true },
{ label: '极速问诊', type: 2, rendered: false },
{ label: '开药问诊', type: 3, rendered: false },
])
// 切换标签页
function onOrderTabChange({ index }) {
tabIndex.value = index
// 每个标签页只被初始一次
orderTabs[index].rendered = true
}
</script>
<template>
<view class="consult-page">
<view class="consult-status-tabs">
<custom-tabs @click="onOrderTabChange" :list="orderTabs"></custom-tabs>
</view>
<!-- 订单列表 -->
<view
v-for="(order, index) in orderTabs"
:key="order.type"
v-show="tabIndex === index"
>
<order-list :type="order.type" v-if="order.rendered" />
</view>
</view>
</template>
以上代码中要关注的重点:
- 标签页显示或隐藏使用
v-show指令 - 标签页的初次渲染使用
v-if指令,当rendered值变为true时表示已经渲染过一次了 - 使用 `:type 属性向标签页组件内传入订单的类型
2.2.3 获取数据
根据接口文档封装调用接口的方法,接口文档地址在这里。
import { http } from '@/utils/http'
// 省略前面小节的代码...
/**
* 医生问诊列表
*/
export const orderListApi = (data) => {
return http.get('/patient/consult/order/list', { params: data })
}
接下来在页面中调用方法获取数据并渲染
<script setup>
import { ref } from 'vue'
import { orderListApi } from '@/services/consult.js'
// 接收组件外部传入的数据
const props = defineProps({
type: {
type: Number,
default: 1,
},
})
// 分页页码
const current = ref(1)
// 每页数据条数
const pageSize = 5
// 订单列表
const orderList = ref([])
// 加载状态提示
const loadingStatus = ref('loading')
// 获取知识列表
async function getOrderList() {
// 正在加载
loadingStatus.value = 'loading'
// 调用接口获取知识列表
const { code, data, message } = await orderListApi({
type: props.type,
current: current.value,
pageSize,
})
// 检测接口是否调用成功
if (code !== 10000) return uni.utils.toast(message)
// 追加方式渲染新请求来的数据
orderList.value = [...orderList.value, ...data.rows]
// 没有更多数据
loadingStatus.value = 'noMore'
}
// 渲染默认标签页数据
getOrderList()
</script>
在上述代码中大家要注意:每次请求的数据是合并进来的,这么做的目的是为后面的分页功能做准备的
最后将请求的数据渲染到组件当中:
<!-- subpkg_consult/order_list/components/order-list.vue -->
<template>
<scroll-view
refresher-enabled
refresher-background="#f6f6f6"
class="uni-scroll-view"
scroll-y
>
<view class="consult-list">
<view
v-for="order in orderList"
:key="order.id"
class="consult-list-item"
>
<view class="consult-header">
<view class="label">
<image class="doctor-avatar" :src="order.docInfo.avatar" />
<text>{{ order.typeValue }}(自动分配医生)</text>
</view>
<text v-if="order.status === 1" class="status color-1">待支付</text>
<text v-if="order.status === 2" class="status color-2">待接诊</text>
<text v-if="order.status === 3" class="status color-2">咨询中</text>
<text v-if="order.status === 4" class="status color-3">已完成</text>
<text v-if="order.status === 5" class="status color-3">已取消</text>
</view>
<navigator
class="consult-body"
hover-class="none"
:url="`/subpkg_consult/order_detail/index?id=${order.id}`"
>
<uni-list :border="false">
<uni-list-item
:border="false"
title="病情描述"
:right-text="order.illnessDesc"
/>
<uni-list-item
:border="false"
title="价格"
:right-text="'¥' + order.payment"
/>
<uni-list-item
:border="false"
title="创建时间"
:right-text="order.createTime"
/>
</uni-list>
</navigator>
<view v-if="order.status === 1" class="consult-footer">
<button class="uni-button minor">取消订单</button>
<button class="uni-button">去支付</button>
</view>
<view v-if="order.status === 2" class="consult-footer">
<button class="uni-button minor">取消订单</button>
<button class="uni-button">继续咨询</button>
</view>
<view v-if="order.status === 3" class="consult-footer">
<button class="uni-button">继续咨询</button>
</view>
<view v-if="order.status === 4" class="consult-footer">
<button class="uni-button minor">问诊记录</button>
<button v-if="order.evaluateId" class="uni-button minor">
查看评价
</button>
<button v-else class="uni-button">去评价</button>
</view>
<view v-if="order.status === 5" class="consult-footer">
<button class="uni-button minor">删除订单</button>
<button class="uni-button">咨询其它医生</button>
</view>
</view>
<!-- 加载状态 -->
<uni-load-more
:status="loadingStatus"
color="#C3C3C5"
:icon-size="16"
:content-text="{
contentdown: '上拉显示更多',
contentrefresh: '数据正在加载中...',
contentnomore: '没有更多数据了',
}"
/>
</view>
</scroll-view>
</template>
不同订单状态专门定义类名,对应关系如下表:
| 订单状态 | 状态值 | 样式类名 | 按钮操作 |
|---|---|---|---|
| 待支付 | 1 | color-1 | 取消订单、去支付 |
| 待接诊 | 2 | color-2 | 取消订单、继续咨询 |
| 咨询中 | 3 | color-2 | 继续咨询 |
| 已完成 | 4 | color-3 | 问诊记录、查看评价/去评价 |
| 已取消 | 5 | color-3 | 删除订单、咨询其它医生 |
2.1.4 分页数据
在移动设备上分页请求数据,常常结合的交互是滚动加载或者叫上拉加载,实现这个交互需要做到 3点:
- 监听页面滚动是否滚动到底部了,在
scroll-view组件上监听的是scrolltolower事件 - 记录每次请求数据的页码,并在请求结束后将页面加 1
- 判断是否还有更多的数据,根据页码数值来判断
<!-- subpkg_consult/order_list/components/order-list.vue -->
<script setup>
import { ref } from 'vue'
import { debounce } from '@/utils'
import { orderListApi } from '@/services/consult.js'
// 省略前面小节的代码...
// 是否还有更多数据
const hasMore = ref(true)
// 分页请求更多数据
const onScrollToLower = debounce(() => {
if (hasMore.value) getOrderList()
}, 400)
// 获取知识列表
async function getOrderList() {
// 省略前面小节的代码...
// 页码加 1
current.value++
// 是否还有更多的数据
hasMore.value = current.value <= data.pageTotal
// 没有更多数据
loadingStatus.value = hasMore.value ? 'more' : 'noMore'
}
// 渲染默认标签页数据
getOrderList()
</script>
<template>
<scroll-view
refresher-enabled
@scrolltolower="onScrollToLower"
refresher-background="#f6f6f6"
class="uni-scroll-view"
scroll-y
>
...
</scroll-view>
</template>
2.1.5 下拉刷新
结合 scroll-view 组件实现下拉刷新交互:
- 监听组件的
@refresherrefresh的事件 - 组件的
refresher-triggered属性开/关下拉动画交互 - 重置页码为 1 和将列表置为
[]
<!-- subpkg_consult/order_list/components/order-list.vue -->
<script setup>
import { ref } from 'vue'
import { orderListApi } from '@/services/consult.js'
// 省略前面小节的代码...
// 是否关闭下拉刷新交互
const refresherTriggered = ref(false)
// 省略前面小节的代码...
// 下拉刷新
async function onScrollRefresh() {
// 重置页码的数据
current.value = 1
// 重置列表数据
orderList.value = []
// 打开下拉动画
refresherTriggered.value = true
// 重新请求数据
await getOrderList()
// 关闭下拉动画
refresherTriggered.value = false
}
// 省略前面小节的代码...
</script>
<template>
<scroll-view
refresher-enabled
:refresher-triggered="refresherTriggered"
@refresherrefresh="onScrollRefresh"
@scrolltolower="onScrollToLower"
refresher-background="#f6f6f6"
class="uni-scroll-view"
scroll-y
>
...
</scroll-view>
</template>
2.2 订单详情(作业)
2.2.1 页面模板
<!-- subpkg_consult/order_detail/index.vue -->
<script setup></script>
<template>
<scroll-page background-color="#f6f6f6">
<view class="consult-page">
<view class="page-header">
<!-- 订单状态 -->
<view class="order-status">
<view class="label">
<text class="title">图文问诊 39元</text>
<text class="tips">
取消/退款进度:您的订单已取消,诊金将在1-7个工作日内退回原支付账户。
</text>
</view>
<text class="status">待支付</text>
</view>
<!-- 医生信息 -->
<view class="consult-doctor">
<text class="lable">服务医生信息</text>
<view class="doctor">
<image
class="doctor-avatar"
src="/static/uploads/doctor-avatar.jpg"
/>
<view class="doctor-info">
<view class="meta">
<text class="name">王医生</text>
<text class="title">内分泌科 | 主任医师</text>
</view>
<view class="meta">
<text class="tag">三甲</text>
<text class="hospital">积水潭医院</text>
</view>
</view>
<view class="arrow">
<uni-icons size="18" color="#C3C3C5" type="forward" />
</view>
</view>
</view>
</view>
<!-- 患者信息 -->
<view class="consult-patient">
<view class="list-title">患者资料</view>
<uni-list :border="false">
<uni-list-item title="患者信息" right-text="李富贵 | 男 | 30岁" />
<uni-list-item title="病情描述" note="头痛,头晕,恶心" />
</uni-list>
</view>
<!-- 订单信息 -->
<view class="order-info">
<view class="list-title">订单信息</view>
<uni-list :border="false">
<uni-list-item title="订单编号" right-text="202201127465" />
<uni-list-item title="创建时间" right-text="2022-01-23 09:23:46" />
<uni-list-item title="取消时间" right-text="2022-01-23 09:23:46" />
<uni-list-item title="应付款" right-text="¥49" />
<uni-list-item title="优惠券">
<template #footer>
<view class="uni-list-text-red">-¥10.00</view>
</template>
</uni-list-item>
<uni-list-item title="积分抵扣">
<template #footer>
<view class="uni-list-text-red">-¥1.00</view>
</template>
</uni-list-item>
<uni-list-item title="实付款">
<template #footer>
<view class="uni-list-text-red">¥39.00</view>
</template>
</uni-list-item>
<uni-list-item title="支付时间" right-text="2022-01-23 09:23:46" />
<uni-list-item title="支付方式" right-text="微信支付" />
</uni-list>
</view>
<view class="notice-bar">
<uni-notice-bar text="请在 00:14:59 内完成支付,超时订单将取消" />
</view>
<!-- 底部 -->
<view class="toolbar">
<template v-if="false">
<view class="total-amount">
需付款: <text class="number">¥39.00</text>
</view>
<view class="buttons">
<button class="uni-button minor">取消问诊</button>
<button class="uni-button">立即支付</button>
</view>
</template>
<template v-else>
<view class="buttons">
<view class="delete-botton">
<uni-icons size="24" color="#848484" type="trash" />
<text>删除订单</text>
</view>
<button class="uni-button">再次购买</button>
</view>
</template>
</view>
</view>
</scroll-page>
</template>
<style lang="scss">
@import './index.scss';
</style>
// subpkg_consult/order_detail/index.scss
.consult-page {
padding-bottom: calc(env(safe-area-inset-bottom) + 155rpx);
.list-title {
margin: 30rpx 0 10rpx;
font-weight: 500;
color: #121826;
}
:deep(.uni-list-item__container) {
padding-left: 0 !important;
padding-right: 0 !important;
}
:deep(.uni-list-item__content-title) {
font-size: 32rpx !important;
color: #3c3e42 !important;
}
:deep(.uni-list-item__extra-text) {
font-size: 32rpx !important;
color: #848484 !important;
}
:deep(.uni-list-item__content-note) {
font-size: 30rpx !important;
color: #848484 !important;
}
:deep(.uni-icon-wrapper) {
padding: 0 !important;
margin-right: -10rpx !important;
font-size: 36rpx !important;
margin-top: 2rpx;
}
}
.page-header {
padding: 20rpx 30rpx 0;
// 需要一个高为300的背景图
background-color: #eaf8f6;
.order-status {
line-height: 1;
padding: 30rpx 0 0 6rpx;
font-size: 32rpx;
position: relative;
.title {
display: block;
margin-bottom: 20rpx;
color: #121826;
font-weight: 500;
}
.tips {
line-height: 1.5;
font-size: 24rpx;
color: #6f6f6f;
}
.status {
position: absolute;
top: 30rpx;
right: 0;
color: #16c2a3;
}
.color-1 {
}
.color-2 {
}
.color-2 {
}
}
.consult-doctor {
position: relative;
bottom: -40rpx;
.title {
font-size: 28rpx;
color: #3c3e42;
}
.doctor {
display: flex;
align-items: center;
height: 160rpx;
padding: 20rpx 30rpx;
margin-top: 20rpx;
border-radius: 20rpx;
background-color: #fff;
box-shadow: 0px 0px 22px 0px rgba(210, 210, 210, 0.5);
position: relative;
.doctor-avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
}
.doctor-info {
height: 100rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
margin-left: 12rpx;
flex: 1;
}
.meta {
display: flex;
align-items: center;
}
.name {
font-size: 36rpx;
color: #3c3e42;
margin-right: 10rpx;
}
.title {
font-size: 24rpx;
color: #6f6f6f;
}
.tag {
line-height: 1;
padding: 8rpx 16rpx 6rpx;
font-size: 22rpx;
color: #fff;
border-radius: 6rpx;
background-color: #677fff;
}
.hospital {
font-size: 26rpx;
color: #3c3e42;
margin-left: 10rpx;
}
}
.arrow {
position: absolute;
top: 50%;
right: 16rpx;
transform: translateY(-50%);
}
}
}
.consult-patient {
padding: 50rpx 30rpx 0;
background-color: #fff;
}
.order-info {
padding: 0 30rpx;
margin-top: 30rpx;
background-color: #fff;
overflow: hidden;
.uni-list-text-red {
color: #eb5757;
}
}
.notice-bar {
text-align: center;
margin-top: 30rpx;
position: sticky;
bottom: calc(env(safe-area-inset-bottom) + 145rpx);
}
.toolbar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
height: 88rpx;
padding: 30rpx 40rpx calc(env(safe-area-inset-bottom) + 30rpx);
background-color: #fff;
.buttons {
flex: 1;
display: flex;
}
.delete-botton {
display: flex;
flex-direction: column;
color: #848484;
text-align: center;
font-size: 24rpx;
margin: 0 60rpx 0 30rpx;
}
.uni-button {
flex: 1;
padding: 0 30rpx;
&.minor {
color: #16c2a3;
margin-right: 20rpx;
border: 1rpx solid #ededed;
background-color: #fafafa;
}
}
.total-amount {
display: flex;
align-items: center;
width: 270rpx;
font-size: 28rpx;
color: #3c3e42;
}
.number {
font-size: 40rpx;
color: #eb5757;
margin-left: 10rpx;
}
}
2.2.2 调用接口
接口文档地址在这里
支付宝支付账号,密码为 111111
scobys4865@sandbox.com
askgxl8276@sandbox.com
超级医生:
https://zhoushugang.gitee.io/patient-h5-note/project/super-doctor.html


