Skip to content

订单支付与问诊室通知(一) ​

本页对应 订单支付与问诊室通知,共 7 段视频,约 1 小时 10 分。

学习目标 ​

  1. 能够将全局支付组件应用到问诊订单页面,并在点击“立即支付”且待付款订单创建成功后调用组件实例的 open 方法显示支付弹层。
  2. 能够通过 ref 获取支付组件实例,并在组件外部调用其通过 defineExpose 暴露的 open、close 方法完成弹层打开与关闭。
  3. 能够为支付组件定义并接收 orderId、amount 两个自定义属性,在页面使用组件时传入订单 ID 与实际支付金额,并在组件内正确渲染金额。
  4. 能够使用 defineEmits 定义 change、close、confirm 三个自定义事件,并在支付方式切换、关闭图标点击、确认支付按钮点击时触发对应事件。
  5. 能够监听支付组件的 change 事件,接收切换后的索引值并更新组件内选中状态,实现微信支付与支付宝支付方式的切换。
  6. 能够监听支付组件的 close 事件,在用户关闭支付弹层时弹出挽留确认框,并根据用户选择执行“继续支付”或调用 close 方法关闭支付组件。

知识点 ​

在问诊订单页接入支付组件 ​

为什么需要:支付弹层不只在当前页面用,所以前面把它封装成全局组件;页面里只要拿到组件实例,就能在“立即支付”后打开它。怎么想:先确认组件已全局注册,页面不用再 import,直接用 ref 拿实例。怎么做:在问诊订单页面放组件,并定义 paymentRef:

html
<custom-payment ref="paymentRef" />
js
const paymentRef = ref(null)

易错点:ref 名称要和模板一致;不要直接操作 DOM,拿组件实例后调用它暴露的方法。转写里调试时先看“立即支付”有没有进入 onPaymentButtonClick,再确认 createOrder 成功后有没有调用 paymentRef.value.open()。

点击立即支付后打开弹层并传金额 ​

为什么需要:用户点“立即支付”时,要先创建待付款订单,再显示支付方式。怎么想:在 onPaymentButtonClick 里,订单创建成功后打开组件;金额从订单信息里的实付款取。怎么做:

js
async function onPaymentButtonClick() {
  const orderId = await createOrder()
  paymentRef.value.open()
}

模板传金额和订单 ID:

html
<custom-payment
  ref="paymentRef"
  :amount="orderInfo.actualPayment"
  :order-id="orderId"
/>

易错点:用了 await 就要给函数加 async;金额字段别写错,转写里调试发现弹层出来了但金额没显示,就是 amount 没传进去。

监听 close 做关闭挽留 ​

为什么需要:用户点叉号可能误关,需要提醒“取消支付后将无法获得医生回复”。怎么想:组件点叉会触发 close 事件,外部监听后弹 uni.showModal。怎么做:

html
<custom-payment ref="paymentRef" @close="onPaymentClose" />
js
function onPaymentClose() {
  uni.showModal({
    title: '关闭支付',
    content: '取消支付后将无法获得医生回复,医生接诊名额有限,今日是否要关闭?',
    cancelText: '仍要关闭',
    confirmText: '继续支付',
    success(res) {
      if (!res.confirm) paymentRef.value.close()
    }
  })
}

易错点:confirm 为 true 是“继续支付”,不做关闭;只有用户选“仍要关闭”时才调 paymentRef.value.close(),别把逻辑写反。

用 defineExpose 暴露 open/close ​

为什么需要:弹层组件内部用 uni-popup,外部不能直接点内部按钮,所以要暴露方法。怎么想:组件内部拿 popupRef,把 open/close 映射出去。怎么做:

js
const popupRef = ref(null)
const open = () => popupRef.value.open()
const close = () => popupRef.value.close()
defineExpose({ open, close })
html
<uni-popup ref="popupRef" :is-mask-click="false">
  ...
</uni-popup>

易错点:defineExpose 给的是对象,键是外部调用名,值是内部真实函数;外部必须先拿到组件实例再调 paymentRef.value.open()。

用 props 接收 orderId 与 amount ​

为什么需要:不同订单的 ID 和金额不同。怎么想:用 defineProps 定义两个属性,金额允许数字或字符串。怎么做:

js
defineProps({
  orderId: { type: String, default: '' },
  amount: { type: [Number, String], default: 0 }
})

模板里显示 amount。易错点:外部传参用 kebab-case,如 :order-id;金额类型别只写 Number,避免字符串金额报错。

用 change 事件同步支付方式切换 ​

为什么需要:微信/支付宝只能选中一个,切换结果要告诉外部。怎么想:用 channelIndex 控制选中,点击列表项时修改索引并 emit('change', index)。怎么做:

js
const emit = defineEmits(['change', 'close', 'confirm'])
const channelIndex = ref(0)

function onChannelChange(index) {
  channelIndex.value = index
  emit('change', index)
}
html
<uni-list-item
  v-for="(channel, index) in channels"
  :key="channel.title"
  clickable
  @click="onChannelChange(index)"
>

易错点:uni-list-item 默认不可点击,必须加 clickable;转写里切换没反应就是漏了它。事件名和外部监听名要一致。

用 close/confirm 事件收口交互 ​

为什么需要:叉号要关闭,立即支付要真正确认支付。怎么想:在组件模板里触发 close,大按钮触发 confirm 并带当前渠道索引。怎么做:

html
<uni-icons type="closeempty" @click="$emit('close')" />
<button @click="$emit('confirm', channelIndex)">立即支付</button>

外部监听:

html
<custom-payment
  ref="paymentRef"
  @close="onPaymentClose"
  @confirm="onPaymentConfirm"
/>

易错点:模板里 $emit 不要漏引号;confirm 要传 channelIndex,否则外部不知道用户选了微信还是支付宝。

在问诊订单页接入支付组件 ​

为什么需要:支付弹层不只在一个页面用,所以把它封装成全局组件 custom-payment,问诊订单页只负责“什么时候打开、传什么数据”。
怎么想:先在页面拿到组件实例,再在“立即支付”创建待付款订单成功后调用 open()。
怎么做:页面中放组件并绑定 ref、金额、订单号和事件;onPaymentButtonClick 里 await createOrder() 后打开弹层。
易错点:ref 名要和脚本一致;组件是全局注册的,不需要重复 import;订单号要在创建订单成功后接收,金额取 orderInfo.actualPayment,漏传会出现金额空白。

vue
<custom-payment
  ref="paymentRef"
  :amount="orderInfo.actualPayment"
  :order-id="orderId"
  @close="onPaymentClose"
  @change="onChannelChange"
  @confirm="onPaymentConfirm"
/>

<script setup>
import { ref } from 'vue'
const paymentRef = ref(null)
const orderId = ref('')
async function onPaymentButtonClick() {
  // 创建待付款订单
  await createOrder()
  paymentRef.value.open()
}
</script>

调试时先看网络请求:点“立即支付”后订单创建成功,再确认弹层是否出现;若不出现,先查 paymentRef.value 是否为空、open 是否已暴露。

关闭支付时的挽留提示 ​

为什么需要:用户点关闭叉号时,要提醒“取消支付后将无法获得医生回复”,避免误关。
怎么想:组件内部点叉号触发 close 事件,页面监听后弹 uni.showModal;点“继续支付”不处理,点“仍要关闭”才调组件 close()。
怎么做:页面监听 @close="onPaymentClose",在方法里用 uni.showModal,根据 res.cancel 关闭弹层。
易错点:uni.showModal 的确认/取消文字要对应“继续支付/仍要关闭”;只有用户仍要关闭时才 paymentRef.value.close(),不要一弹提示就关。

js
function onPaymentClose() {
  uni.showModal({
    title: '关闭支付',
    content: '取消支付后将无法获得医生回复。医生接诊名额有限。',
    cancelText: '仍要关闭',
    confirmText: '继续支付',
    success(res) {
      if (res.cancel) paymentRef.value.close()
    }
  })
}

用 defineExpose 开放 open/close 方法 ​

为什么需要:弹层开关由页面控制,组件必须把内部方法暴露给外部。
怎么想:defineExpose 的 key 是外部调用名,value 是组件内部真实函数;内部再调 popupRef.value.open()/close()。
怎么做:组件内定义 open、close,最后 defineExpose({ open, close })。
易错点:不要直接操作弹层实例;外部必须通过组件实例调用;组件未挂载时调用会拿不到实例,通常放在按钮点击后调用。

js
const popupRef = ref(null)
function open() { popupRef.value.open() }
function close() { popupRef.value.close() }
defineExpose({ open, close })

用 defineProps 接收订单号和金额 ​

为什么需要:不同订单的订单号、支付金额不同,必须由外部传入。
怎么想:订单号用 String,金额兼容数字和字符串并给默认值 0。
怎么做:defineProps 定义 orderId、amount,模板中显示 &#123;&#123; amount &#125;&#125;。
易错点:金额没传会显示空;模板里可直接用 amount,脚本里用 props.amount;属性名要和页面传参一致。

js
const props = defineProps({
  orderId: String,
  amount: { type: [Number, String], default: 0 }
})

用 defineEmits 定义 change/close/confirm 事件 ​

为什么需要:组件内部切换支付方式、点关闭、点确认,都要把结果交给页面。
怎么想:先 defineEmits(['change','close','confirm']),再在对应点击处 emit;change 传索引,confirm 传当前渠道索引。
怎么做:列表项加 clickable 和 @click,关闭图标触发 close,支付按钮触发 confirm。
易错点:uni-list-item 默认不可点击,漏加 clickable 会导致点击无反应;调试时用 console.log 验证 emit('change', index) 的 0/1 是否传到页面。

js
const emit = defineEmits(['change', 'close', 'confirm'])
const channelIndex = ref(0)

function onChannelChange(index) {
  channelIndex.value = index
  emit('change', index)
}
function onClose() { emit('close') }
function onConfirm() { emit('confirm', channelIndex.value) }

模板关键处:

vue
<uni-list-item
  clickable
  v-for="(item, index) in channelList"
  :key="item.title"
  @click="onChannelChange(index)"
/>
<text @click="onClose">×</text>
<button @click="onConfirm">立即支付</button>

调试过程中还遇到:await 所在函数漏写 async 会报语法错误;演示时订单只能用一次,需要重新创建订单再测。

先明确:支付组件为什么要“开放方法” ​

支付弹层不是在组件内部自己弹出来的,而是用户在问诊订单页点击“立即支付”后才显示。
为什么需要:组件内部只负责弹层外观和交互,真正触发时机在父页面。
怎么想:父页面要拿到组件实例,再调用组件暴露出来的 open、close。
怎么做:组件内用 defineExpose 暴露方法,父页面用 ref 获取实例。
易错点:不要直接操作组件内部 popupRef;父页面调用前要确保组件已挂载,通常在按钮点击事件里调用。

vue
<!-- 支付组件内部:开放 open / close -->
<script setup>
import { ref } from 'vue'

const popupRef = ref(null)

function open() {
  popupRef.value?.open()
}

function close() {
  popupRef.value?.close()
}

defineExpose({ open, close })
</script>

defineExpose 的语法是“外部调用的方法名:组件内部真实函数”。如果名字一样,可以简写为 defineExpose({ open, close })。父页面中组件若是全局注册,无需单独导入:

vue
<template>
  <payment-popup
    ref="paymentRef"
    :order-id="orderId"
    :amount="actualPayment"
    @change="onPaymentChange"
    @close="onPaymentClose"
    @confirm="onPaymentConfirm"
  />
</template>

<script setup>
import { ref } from 'vue'

const paymentRef = ref(null)
const orderId = ref('')
const actualPayment = ref(0)

async function onPaymentButtonClick() {
  // 创建待付款订单后,再打开支付组件
  // const res = await createOrder()
  // orderId.value = res.orderId
  paymentRef.value?.open()
}
</script>

组件基础用法:先理解 uni-popup 的蒙层 ​

转写中先临时加按钮验证组件:点按钮调用 open 显示弹层。
为什么需要:支付弹层要能打开、关闭,还要控制点击蒙层是否关闭。
怎么想:isMaskClick 控制蒙层点击后是否关闭;@maskClick 监听蒙层点击。
怎么做:先设 isMaskClick=false,点击蒙层事件会触发但弹层不消失;设为 true 时点击蒙层会关闭。
易错点:测试按钮只是临时验证,正式使用时删掉;不要把它当成业务入口。

vue
<payment-popup :is-mask-click="false" @mask-click="onMaskClick" />

定义属性:订单号和金额必须由外部传入 ​

为什么需要:不同订单支付不同金额,组件不能写死。
怎么想:父向子传数据用自定义属性。
怎么做:用 defineProps 定义 orderId 和 amount,金额允许数值或字符串,默认 0。
易错点:父组件传金额时注意是否加冒号;amount 是变量要写 :amount="actualPayment",纯数字可写 amount="39.9"。

vue
<script setup>
const props = defineProps({
  orderId: {
    type: String,
    default: ''
  },
  amount: {
    type: [Number, String],
    default: 0
  }
})
</script>

模板中显示金额:

vue
<text>{{ amount }}</text>

父页面从订单信息中取实付款,例如 orderInfo.actualPayment,并传给组件。

定义事件:change、close、confirm ​

为什么需要:组件内部切换支付方式、点击关闭、点击确认支付,父页面都要知道。
怎么想:子向父传结果用自定义事件。
怎么做:defineEmits 定义事件;点击时 emit 触发;父页面用 @change、@close、@confirm 监听。
易错点:事件名必须一致;emit 第二个参数才是传给父页面的数据。

vue
<script setup>
import { ref } from 'vue'

const emit = defineEmits(['change', 'close', 'confirm'])
const channelIndex = ref(0)

const channelList = [
  { title: '微信支付', thumb: '/static/pay-wechat.png' },
  { title: '支付宝支付', thumb: '/static/pay-alipay.png' }
]

function onChannelChange(index) {
  channelIndex.value = index
  emit('change', index)
}
</script>

渠道列表关键结构示意,注意 uni-list-item 默认不可点击,要加 clickable:

vue
<uni-list>
  <uni-list-item
    v-for="(channel, index) in channelList"
    :key="channel.title"
    clickable
    @click="onChannelChange(index)"
  >
    <image :src="channel.thumb" alt="支付渠道图标" />
    <text>{{ channel.title }}</text>
    <uni-icons v-if="channelIndex === index" type="checkmarkempty" />
    <uni-icons v-else type="circle" />
  </uni-list-item>
</uni-list>

小叉叉触发关闭,确认支付按钮触发确认:

vue
<uni-icons type="closeempty" @click="emit('close')" />
<button type="primary" @click="emit('confirm', channelIndex)">立即支付</button>

调试时如果点击渠道没有反应,先检查是否忘记加 clickable;如果父页面没收到事件,检查 defineEmits、emit 名称和父组件监听名称是否一致。

父页面监听 close:做关闭支付挽留 ​

为什么需要:用户点关闭时,要提醒“取消支付后将无法获得医生回复”。
怎么想:父页面监听 @close,弹 uni.showModal;只有用户点“仍要关闭”才调用组件 close。
怎么做:confirm 为 true 表示继续支付,不关闭;否则调用 paymentRef.value?.close()。
易错点:不要一收到 close 就直接关弹层,否则没有挽留机会。

vue
function onPaymentClose() {
  uni.showModal({
    title: '关闭支付',
    content: '取消支付后将无法获得医生回复,医生接诊名额有限,确定要关闭吗?',
    cancelText: '仍要关闭',
    confirmText: '继续支付',
    success(res) {
      if (!res.confirm) {
        paymentRef.value?.close()
      }
    }
  })
}

function onPaymentChange(index) {
  console.log('当前支付渠道索引:', index)
}

function onPaymentConfirm(index) {
  console.log('确认支付,渠道索引:', index)
}

这一节的完整流程 ​

先在支付组件内用 uni-popup 做弹层,用 defineExpose 开放 open、close;用 defineProps 接收 orderId、amount;用 defineEmits 定义 change、close、confirm。父页面在“立即支付”回调里创建订单后,通过 paymentRef.value?.open() 打开组件,并监听三个事件。关闭时先弹挽留框,用户坚持关闭才调用组件 close。这样支付组件就能被问诊订单页复用了。

代码示例 ​

代码1:支付弹层组件 payment-popup.vue(组件内部:props / emits / expose) ​

vue
<!-- components/payment-popup/payment-popup.vue -->
<template>
  <uni-popup ref="popupRef" type="bottom" :is-mask-click="false" @mask-click="onMaskClick">
    <view class="payment-container">
      <view class="payment-header">
        <text class="title">选择支付方式</text>
        <uni-icons type="closeempty" size="20" @click="emit('close')" />
      </view>

      <view class="payment-amount">
        <text class="label">支付金额</text>
        <text class="value">¥{{ amount }}</text>
      </view>

      <uni-list>
        <uni-list-item
          v-for="(item, index) in channelList"
          :key="item.title"
          clickable
          @click="onChannelChange(index)"
        >
          <template #body>
            <view class="channel-item">
              <image class="thumb" :src="item.thumb" />
              <text class="channel-title">{{ item.title }}</text>
              <uni-icons
                :type="channelIndex === index ? 'checkbox-filled' : 'circle'"
                size="20"
                :color="channelIndex === index ? '#16a085' : '#999'"
              />
            </view>
          </template>
        </uni-list-item>
      </uni-list>

      <button class="pay-btn" type="primary" @click="emit('confirm', channelIndex)">
        立即支付
      </button>
    </view>
  </uni-popup>
</template>

<script setup>
import { ref } from 'vue'

// 1. 定义属性:订单ID + 金额
const props = defineProps({
  orderId: {
    type: String,
    default: ''
  },
  amount: {
    type: [Number, String],
    default: 0
  }
})

// 2. 定义事件:切换 / 关闭 / 确认支付
const emit = defineEmits(['change', 'close', 'confirm'])

const popupRef = ref(null)

// 3. 支付渠道数据(微信 / 支付宝)
const channelList = ref([
  { title: '微信支付', thumb: '/static/weixin.png' },
  { title: '支付宝支付', thumb: '/static/zhifubao.png' }
])

// 当前选中的支付方式索引
const channelIndex = ref(0)

// 切换支付方式:改内部选中状态 + 通知外部
const onChannelChange = (index) => {
  channelIndex.value = index
  emit('change', index)
}

// 蒙层点击:is-mask-click 为 false 时事件仍会触发,但弹层不消失
const onMaskClick = () => {
  // 此处按需处理,讲义中此处未做额外操作
}

// 4. 开放给组件外部调用的方法
const open = () => {
  popupRef.value.open()
}

const close = () => {
  popupRef.value.close()
}

defineExpose({ open, close })
</script>

<style lang="scss" scoped>
.payment-container {
  padding: 20px;
  background-color: #fff;
  border-radius: 12px 12px 0 0;

  .payment-header {
    display: flex;
    justify-content: space-between;
    align-items: center;

    .title {
      font-size: 16px;
      font-weight: 600;
    }
  }

  .payment-amount {
    margin: 20px 0;
    text-align: center;

    .label {
      color: #999;
      font-size: 12px;
    }

    .value {
      display: block;
      margin-top: 6px;
      font-size: 26px;
      font-weight: 600;
    }
  }

  .channel-item {
    display: flex;
    align-items: center;

    .thumb {
      width: 24px;
      height: 24px;
      margin-right: 8px;
    }

    .channel-title {
      flex: 1;
    }
  }

  .pay-btn {
    margin-top: 20px;
  }
}
</style>

关键行 / 易错点

  • defineExpose({ open, close }):key 是外部调用名,value 是组件内部真实函数;组件外部必须先通过 ref 拿到组件实例,才能 .open() / .close()。
  • open 内部是 popupRef.value.open(),不要漏 .value,否则调的是 ref 对象本身。
  • uni-list-item 默认点击事件是被禁用的,必须加 clickable,否则 @click 不生效(转写稿里就是漏了这个导致切换没反应)。
  • is-mask-click="false" 时,点蒙层事件仍触发,只是弹层不消失,所以关闭要由外部显式调 close()。
  • amount 类型用 [Number, String],因为后端返回的金额可能是字符串,写死 Number 会警告。
  • :key="item.title" 保证不重复即可。

代码2:问诊订单页面使用组件(立即支付 → 打开弹层 → 关闭挽留) ​

vue
<!-- pages/consult/order.vue(节选) -->
<template>
  <view class="page">
    <!-- 订单信息等省略 -->
    <view class="footer">
      <text class="pay-amount">实付款:¥{{ orderInfo.actualPayment }}</text>
      <button type="primary" @click="onPaymentButtonClick">立即支付</button>
    </view>

    <!-- 支付组件:全局注册,无需单独 import -->
    <custom-payment
      ref="paymentRef"
      :order-id="orderId"
      :amount="orderInfo.actualPayment"
      @close="onPaymentClose"
      @change="onPaymentChange"
      @confirm="onPaymentConfirm"
    />
  </view>
</template>

<script setup>
import { ref } from 'vue'

// 组件实例(后缀 REF 表示组件实例)
const paymentRef = ref(null)

// 创建订单后拿到的订单ID
const orderId = ref('')
// 上一步生成的待付款订单信息
const orderInfo = ref({ actualPayment: 0 })

// 点击“立即支付”
const onPaymentButtonClick = async () => {
  // await createOrder() —— 生成待付款订单,拿到 orderId 与实付金额
  orderId.value = 'order_123456'
  orderInfo.value = { actualPayment: 39.9 }

  // 打开支付弹层(单标签写法,后面可省结束标签)
  paymentRef.value.open()
}

// 用户点“×”关闭:先挽留
const onPaymentClose = async () => {
  const res = await uni.showModal({
    title: '关闭支付',
    content: '取消支付后将无法获得医生回复,医生接诊名额有限。',
    cancelText: '仍要关闭',
    cancelColor: '#999999',
    confirmText: '继续支付',
    confirmColor: '#16a085'
  })
  // 点“继续支付”时 res.confirm 为 true,什么都不做
  // 点“仍要关闭”时 res.confirm 为 false,才真正关闭弹层
  if (!res.confirm) {
    paymentRef.value.close()
  }
}

// 支付方式切换
const onPaymentChange = (index) => {
  console.log('内部切换了支付方式,当前索引:', index)
}

// 点击“立即支付”大按钮
const onPaymentConfirm = (index) => {
  console.log('确认支付,支付渠道索引:', index, '订单:', orderId.value)
}
</script>

关键行 / 易错点

  • 组件已全局注册,页面里不用 import,直接 <custom-payment ref="paymentRef" ... />。
  • onPaymentButtonClick 里有 await,函数必须声明为 async,否则报语法错误(转写稿里就踩了这个坑)。
  • 属性名用中划线 :order-id、:amount,金额来自上一步的 orderInfo.actualPayment。
  • uni.showModal 的“仍要关闭”是取消按钮(cancelText),所以判断是 if (!res.confirm) 才 close(),写成 res.confirm 逻辑就反了。
  • 订单只能用一次,测试时要重新走一遍创建订单流程,别拿旧订单反复点。

代码3:最小验证示例(defineExpose 开放方法 → 父组件 ref 调用) ​

vue
<!-- pages/test/test.vue -->
<template>
  <view>
    <!-- 组件外部按钮:打开弹层 -->
    <button type="primary" @click="openPayment">组件外部打开弹层</button>

    <custom-payment ref="paymentRef" order-id="test_001" :amount="39.9" />
  </view>
</template>

<script setup>
import { ref } from 'vue'

// 获取组件实例
const paymentRef = ref(null)

// 点击时调用组件内部通过 defineExpose 开放的 open 方法
const openPayment = () => {
  paymentRef.value.open()
}
</script>

关键行 / 易错点

  • 流程:外部拿组件实例(ref)→ 调 open() → 组件内部执行 popupRef.value.open() → 弹层显示。
  • 不要在 onMounted 之前/组件还没挂载时调 open(),所以演示里放在按钮点击里调,此时组件一定已挂载完。
  • 名字要对应:defineExpose 里叫什么,外部就 .什么(),改名两处都要改(讲义里组件名也统一成了 payment-popup)。
  • 组件内部自带的测试按钮和测试 @mask-click 监听可以删掉,只保留对外开放的方法,避免干扰。

常见错误 ​

  1. 现象:点击“立即支付”后报错 Cannot read properties of null (reading 'open') → 原因:父组件 ref 尚未绑定到已挂载的组件实例,或调用时机早于组件挂载 → 修法:确认模板中已写 ref="paymentRef",并在按钮点击等挂载后时机再调用 paymentRef.value.open()。

  2. 现象:子组件点击支付方式无反应,change 事件不触发 → 原因:uni-list-item 默认不可点击,或点击事件未绑定到实际可点击项 → 修法:给列表项加 clickable,并在对应项上绑定点击回调后触发自定义事件。

  3. 现象:父组件监听了 close/confirm/change,但回调不执行 → 原因:子组件 defineEmits 声明名、emit 触发名、父组件监听名三者不一致,或只声明未触发 → 修法:统一事件命名,逐项核对声明、触发、监听位置。

  4. 现象:支付弹层金额或订单号显示为空/不正确 → 原因:父组件未传 amount、orderId,或属性名大小写、拼写不一致 → 修法:核对 defineProps 与父组件模板传参名称,确认数据来源字段正确。

  5. 现象:点关闭叉号后直接关闭,没有“继续支付/仍要关闭”挽留逻辑 → 原因:只监听了 close,未在父组件区分确认结果,或判断分支写反 → 修法:在关闭回调中弹出确认框,仅在用户选择“仍要关闭”时调用组件 close(),选择“继续支付”则不关闭。

练习 ​

  1. 在问诊订单页面点击“立即支付”后打开支付组件,并传入当前订单 orderId 与实付金额 amount,验证弹层展示正确。
  2. 在支付组件内实现微信/支付宝切换:点击某项后更新选中态,并向父组件传出当前索引。
  3. 父组件监听支付组件的 close 事件,实现“继续支付 / 仍要关闭”挽留流程,并验证两个分支行为。
  4. 排查并修复一个场景:uni-list-item 已绑定点击事件但控制台无输出,要求说明检查点与修复步骤。
  5. 父组件分别监听 change、confirm、close 三个事件,打印或处理对应数据,验证事件传参是否符合预期。

课堂画面 ​

课堂画面 01

课堂画面 02

课堂画面 03

课堂画面 04

课堂画面 05

课堂画面 06

原视频对照 ​

#视频时长
1优医咨询-问诊订单支付-109:20
2优医咨询-问诊订单支付-205:45
3优医咨询-支付组件开放方法17:14
4优医咨询-支付组件定义属性08:24
5优医咨询-支付组件定义事件-109:29
6优医咨询-支付组件change事件14:24
7优医咨询-支付组件close和confirm事件06:19

小结 ​

  1. 组件联调先查三件事:属性是否传对、事件是否对齐、方法是否在挂载后调用。
  2. ref 调用组件方法前,必须确认模板绑定存在且实例已可用。
  3. 支付组件的关键契约是:数据向内传、事件向外抛、方法供外部调用。
  4. 支付方式切换应由索引状态驱动,事件负责把切换结果同步给父组件。
  5. 关闭挽留逻辑必须区分“继续支付”和“仍要关闭”,不能把确认框结果当成关闭指令直接执行。
  6. 列表项点击无效时,优先检查组件是否显式开启可点击能力。
  7. 页面联调顺序建议:先验 props,再验事件,再验方法,最后走完整支付流程。