• 引入
  • 代码演示
    • 基本
    • 使用插槽及其他配置
  • API
    • Cashier Props
    • Cashier Methods
      • next(scene, option)
    • Captcha Slots
      • header
      • footer
      • channel
      • payButton
      • scene
    • Cashier Events
      • @select(item: {text, value})
      • @pay(item: {text, value})
      • @cancel()
      • @show()
      • @hide()

    Cashier 收银台

    Scan me!

    业务支付弹窗,支持支付渠道选择和支付验证码发送

    引入

    1. import { Cashier } from 'mand-mobile'
    2. Vue.component(Cashier.name, Cashier)

    代码演示

    基本

    Cashier 收银台 - 图2

    1. <template>
    2. <div class="md-example-child md-example-child-cashier">
    3. <md-field
    4. title="支付结果"
    5. >
    6. <md-radio-list
    7. v-model="cashierResult"
    8. :options="cashierResults"
    9. />
    10. </md-field>
    11. <md-field
    12. title="支付配置"
    13. >
    14. <md-input-item
    15. title="支付金额"
    16. align="right"
    17. type="money"
    18. v-model="cashierAmount"
    19. >
    20. </md-input-item>
    21. <md-field-item
    22. title="发送验证码"
    23. align="right"
    24. >
    25. <md-switch v-model="isCashierCaptcha"></md-switch>
    26. </md-field-item>
    27. </md-field>
    28. <md-button @click="isCashierhow = !isCashierhow">{{ isCashierhow ? '收起收银台' : '唤起收银台' }}</md-button>
    29. <md-cashier
    30. ref="cashier"
    31. v-model="isCashierhow"
    32. :channels="cashierChannels"
    33. :channel-limit="2"
    34. :payment-amount="cashierAmount"
    35. payment-describe="关于支付金额的特殊说明"
    36. large-radius
    37. @select="onCashierSelect"
    38. @pay="onCashierPay"
    39. @cancel="onCashierCancel"
    40. ></md-cashier>
    41. </div>
    42. </template>
    43. <script>
    44. import {Button, RadioList, Field, FieldItem, InputItem, Switch, Cashier, Toast} from 'mand-mobile'
    45. export default {
    46. name: 'cashier-demo',
    47. components: {
    48. [Button.name]: Button,
    49. [RadioList.name]: RadioList,
    50. [Field.name]: Field,
    51. [FieldItem.name]: FieldItem,
    52. [InputItem.name]: InputItem,
    53. [Switch.name]: Switch,
    54. [Cashier.name]: Cashier,
    55. },
    56. data() {
    57. return {
    58. isCashierhow: false,
    59. isCashierCaptcha: false,
    60. cashierAmount: '100.00',
    61. cashierResult: 'success',
    62. cashierResults: [
    63. {
    64. text: '支付成功',
    65. value: 'success',
    66. },
    67. {
    68. text: '支付失败',
    69. value: 'fail',
    70. },
    71. ],
    72. cashierChannels: [
    73. {
    74. icon: 'cashier-icon-1',
    75. text: '招商银行(0056)',
    76. value: '001',
    77. },
    78. {
    79. icon: 'cashier-icon-2',
    80. text: '支付宝支付',
    81. value: '002',
    82. },
    83. {
    84. icon: 'cashier-icon-3',
    85. text: '微信支付',
    86. value: '003',
    87. },
    88. {
    89. icon: 'cashier-icon-4',
    90. text: 'QQ钱包支付',
    91. value: '004',
    92. },
    93. {
    94. icon: 'cashier-icon-5',
    95. text: '一网通支付',
    96. value: '005',
    97. },
    98. ],
    99. }
    100. },
    101. computed: {
    102. cashier() {
    103. return this.$refs.cashier
    104. },
    105. },
    106. methods: {
    107. doPay() {
    108. if (this.isCashierCaptcha) {
    109. this.cashier.next('captcha', {
    110. text: 'Verification code sent to 156 **** 8965',
    111. brief: 'The latest verification code is still valid',
    112. autoCountdown: false,
    113. countNormalText: 'Send Verification code',
    114. countActiveText: 'Retransmission after {$1}s',
    115. onSend: countdown => {
    116. console.log('[Mand Mobile] Send Captcha')
    117. this.sendCaptcha().then(() => {
    118. countdown()
    119. })
    120. },
    121. onSubmit: code => {
    122. console.log(`[Mand Mobile] Send Submit ${code}`)
    123. this.checkCaptcha(code).then(res => {
    124. if (res) {
    125. this.createPay().then(() => {
    126. this.cashier.next(this.cashierResult)
    127. })
    128. }
    129. })
    130. },
    131. })
    132. } else {
    133. this.createPay().then(() => {
    134. this.cashier.next(this.cashierResult, {
    135. buttonText: '好的',
    136. handler: () => {
    137. this.isCashierhow = false
    138. Toast.info(`${this.cashierResult}点击`)
    139. },
    140. })
    141. })
    142. }
    143. },
    144. // Create a pay request & check pay result
    145. createPay() {
    146. this.cashier.next('loading')
    147. return new Promise(resolve => {
    148. this.timer = setTimeout(() => {
    149. resolve()
    150. }, 3000)
    151. })
    152. },
    153. // Create a captcha sending request
    154. sendCaptcha() {
    155. return new Promise(resolve => {
    156. this.timer = setTimeout(() => {
    157. resolve()
    158. }, 200)
    159. })
    160. },
    161. // Create a captcha checking request
    162. checkCaptcha(code) {
    163. return new Promise(resolve => {
    164. this.timer = setTimeout(() => {
    165. resolve(!!code)
    166. }, 200)
    167. })
    168. },
    169. onCashierSelect(item) {
    170. console.log(`[Mand Mobile] Select ${JSON.stringify(item)}`)
    171. },
    172. onCashierPay(item) {
    173. console.log(`[Mand Mobile] Pay ${JSON.stringify(item)}`)
    174. this.doPay()
    175. },
    176. onCashierCancel() {
    177. // Abort pay request or checking request
    178. this.timer && clearTimeout(this.timer)
    179. },
    180. },
    181. }
    182. </script>
    183. <style lang="stylus">
    184. .md-example-child-cashier
    185. .md-field
    186. margin-bottom 30px
    187. .md-cashier-channel-item
    188. .item-icon.cashier-icon-1
    189. background url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAABHklEQVR4Ae2WgUYEURSGh70oQIB6gh6gHmBsqhfqHdq2hAJTUYAACMCytqQISUDAVpsEsWi09TX/XFcxYeJ0E/vzMf4z7uc6cJMQWi6l1egU5AUYkfszXZp8TVFmBfwyWbhZU0UcXKrbdWMJ5ZLwNaIwl5CY1BPen8HgAtoT1dn6pJ/dnRoKn64p01mpztQpj1eGwsNlyuTPsDX92W/P+A70j6FQ3BxR5nI3dP4bNDPeochmYZTD+xvsz8HBvP8evWhmLvScb1Cm3/Mo6sLcXLg5BcMHQhgO1BkL+8fUjP6NLLw9MRB+R0h19h+F4x3WYSystVe/rx/zJ0+MXtxHVNstxBO6ZqKw2tiLINxJQrzULenKxjvVWV3W3GLwfAD9KR4TBA12SgAAAABJRU5ErkJggg==') center no-repeat
    190. background-size 26px
    191. .item-icon.cashier-icon-2
    192. background url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAB7ElEQVR4Ac2WA6weQRSFb62gtuPajetGtW0FNcO6cVK7XdRu2Ia149R2Z6bP9r6T+yb5bZ7k/Bh9gzt3l1hXfzUiS+4iU36GK2AnTq6AP/PYJ/42JhZ+oPAp7CTWYDDUVHu4IDneCaD8kkTgV4r4zA5+akBuivhMI56llyLv768wXkpfYNK3NGXAaHQhpxlHY9KAlpwc6Qojt6W2urZYnYoFWA5XhWhTSaf/dmHYDqc2/v+NFPiZDLmKzv7pxAPAZIvWZPwbjrqN8B242LU6edUtgMZGGjS3XI8R6IRTj/yI2xhiPNofA7CPCyiuRAL8wM9FUy6AH8I5urwE/gwb8FSdS311+ldz1KsIgGIpmWpNGEGSQYbaC7f1gWIyPClD3uaJBgWeld3x/S3MyCwkWwz1AfqsWCzV8EIfIAeJqd6GAcuHx7oFylz4AVlqGl116gQ/c3HABTTkFH1pywPD1Esy//V0wdQMlJe6Rex3MtQ2OvO/fRgZXbziGVqqP+CmXm0m/AED2WT9G0GOU4sg/sYVCZLCyngrLTHRJ9K9Gl4jWzShYDoj+5Ih7keQiSQWcwgTHqKBPrP8wanK/D+QLzwOH4DeOgDu+maeiPwmBS9RuFNJA1pyV9JfhF33BHS9vZVxhFTWjCn2kYZVAyRlGm3AoxGeAAAAAElFTkSuQmCC') center no-repeat
    193. background-size 26px
    194. .item-icon.cashier-icon-3
    195. background url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAYCAYAAADpnJ2CAAABiklEQVR4AbWVAWRCURSGf8gIGQyYgUEAgyAAbCCAAAQQGBAYIGAwAAgIwAMJGIsqRTAgQPAQEsKDu//kXHLddV+v+w6f0HnvP/e8/5yLYPzgAXN0yICsSUoyZUeW+l/nlFs45miSRF9scpKRBDM0rhGqacXmRobhE8/wwsQtMZHYyjv/E2swYU9MZPZW1DVGWoLYEQt88ffR/W7DyEIHCn3ab4gVKpjhzbayHlOI9K2QHqZFNsSctKSSCEI78oE17h1PTJy8PnRwjYdNjlanpEdqsLHC04XnltA2+KuWiudoe9y7xQLvWKF6JlSVE5DjxZZf3CRs91nV3zqjXYxw55iuk9PlGQKJGaZ4hoR123lM8cqc3yu+dSqC40BS4lkSdf9zQcY4tSic2LQLQgc5K+jmrl3Wu0DiUkxy4+o72LEJnDISUrDjtKREwTHc0DkalSA2sfPqE62oKWKJDWRmgzd+BKENaUGjLMGjzmbbLoiigmvSk/tMnKa3y0DQ9vdIM9S6gKCK6FqLGX+Ik2Cgy7oRZQAAAABJRU5ErkJggg==') center no-repeat
    196. background-size 26px
    197. .item-icon.cashier-icon-4
    198. background url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAcCAYAAAB75n/uAAADaElEQVR4AWIgADiAOAaI1wDxUxZAadUA5GoTBCd59Wzbtm3btm3btm3btm3btt/5+v/6uLXRl/q7qu+SRc/OzKbXIhAh5ZXB1QZLyf8ANz+noAseNZhc3ETb4NOaocUiX+JFlYJiEuU1cdNBzGQSjbVWN3bu3BkHDx5EgQIFQsb69u2LjRs3IkWKFHqgE+ICndUNFCB27drFIBxDoUKFcP/+fZLj9rIpL05wVV1co0YNEBQdP3484sWLx9OjXbt2JD5//mwvwBaHV1KvPctCsEyLFy9GxIgRQeErV67g2rVrePz4sf1eOEBGLtB56tQpEPPmzQO/x4gRAx8+fADRvXt3R02PJnaQw97inKnCYkiLTBjf2IJ5bQTkxJax0KdpdiSPK44CxBcdk5LE7qqWaGxDwZv5AmxyzjtTLKiVP3QfNfakTVBZdDzOHHPDhESREBxkQA3Br4Vh8WlyJAr568Leq634MC4yPs63onz2UPEVySL7vswcc6LoeJIx+vPXmWNgW8ooSBs+DDcgY2LBoYEWvBoaDVeaJMSNlvFD+G5sZJYLsaMGlTJiGOxPFRXUuJ8x+nnREO16+ui+nAzm4qSRUT5qWES2Bpbg7OCwuNcjJok1nawMzjlUix4Oq5NHhrr3WJqonnqj5/HUJ9IEnkAnx5n6rMSRQFLQ0VpmkSSslVlNkyAkD647/y8pkx9vC6XiYrf4tnh6dEocHapWsDdNVK/Y/Pnz8XvdYlOiH6rlx7fh3eFx8iDg54uyZcvq13W8jT1s3boVPyYPVYXg/+8vfJ4/htedayT42d/TAzoaNmyoB7gquj3QJXkqNQBPZwZ6gCBtgcpp06bZBPD79QMmQFNUhYMpj4M+dOYEHdT3y0f8nDsBX3u3wpdujU1l4OHhgQgRIsBqld+K7VNbBgU1mlhNx6SZuYtVq1ZRUL2e04TaGuLTbmnP7uDv379Ily4dxZ/bOqkGPt5Mk802i+bNm4MHy5ZccokzYJOMN/jn6WxBscxhwVfM29sbjsBSsmclMoc47x9q2BffLP00x/TfM0DQvkYqjB45BHyT+Q7zNeNvpWuntmhUIgq4RtsHaokObJS2nHRAf2a1s5/s3NFXtvOzauE6qeWoRHUMHjT4mQJBfGpws8HKyrrKHOOcso57DlJDFPwHtUxGlWBNgLkAAAAASUVORK5CYII=') center no-repeat
    199. background-size 26px
    200. .item-icon.cashier-icon-5
    201. background url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAMAAABF0y+mAAABPlBMVEUAAAC/AEDIFS3GFS3HFS3HFS3HFS3MADPGFC7GFS3FFSvHFS3GFS3HFS3HFS3KIjnooKnmmKPJHDTMLELqqLHmlaDIGjHONEnssLjkjJjHFi7////hgI3rqrLdcoDwwsjUS17KITjed4T44+XIGC/LJz3++fr119vQPFD//v7NMEXgf43ca3r78PLZX3DVUWPpoavut77IGTD44+bxxszmlqHxw8nHFy/44OPttr3LJjz99/jaZXXXWWrhgY7+/P3//f3XV2jzzdPgf4zut7/nmqTxxMrllJ/yyM7dcH/++vvMK0HWUmTQO0/ZYXHba3r77vDJHTTts7v22Nzxxcv55OffdoXed4XRP1PedIPaZHThg5D11dnVTmDJHzb77e/55+rLJj377/Hqp7DkjprTRlnlkZzRPlLNL0TXVmdZnHwTAAAAD3RSTlMABEqUzPH/BXDmMNZV+P145yZGAAABMklEQVR4AXTQ02KcARCG4Z/v2t5vVdv2to1t2/d/BcnEfE7H45xxPT8IIQx8z3WuikRjnItFI5dj8QRXJOIXsWSKa1LJ81iaG9Kn0XiKW6TiFoskuFXCtopyh+jRfTFMJpvLA4ViqQxUqrU6MdfxwJSkBtCUWkBb6oDn+Jh796UHD3n0WHoCT6Vnz8F3AsyLl6+k1zQk6Q1vpXdA4IQWe//h4yfp85evFvz2/YdlQOhgfkq/nkm/pT9S96/1NifBf9L/HpnePql/QIMYa0tHGhoekRkdk16NT0yCtQ2AKalIfVrSDO9npbl5TGCnPO9KC7AoaQmWpRUwvj1hVWoDa+vaKMPmlrbBePa+LzvVCsDu3j7AweEyxSAJghFfwOONMnyRjS+Z4Etg+JIm4USNNzsAAPHdK2mIKv4bAAAAAElFTkSuQmCC') center no-repeat
    202. background-size 26px
    203. </style>
    204.  

    使用插槽及其他配置

    Cashier 收银台 - 图3

            <template>
      <div class="md-example-child md-example-child-cashier">
        <md-button @click="isCashierhow = !isCashierhow">{{ isCashierhow ? '收起收银台' : '唤起收银台' }}</md-button>
        <md-cashier
          ref="cashier"
          v-model="isCashierhow"
          :channels="cashierChannels"
          :payment-amount="cashierAmount"
          payment-describe="关于支付金额的特殊说明"
          large-radius
          @show="onCashierShow"
          @select="onCashierSelect"
          @pay="onCashierPay"
          @cancel="onCashierCancel"
        >
          <div slot-scope="{ scene }" slot="header">
            <md-notice-bar
              v-if="scene === 'choose'"
              mode="closable"
              icon="warn"
              type="warning"
            >
              该银行3:00-12:00系统维护,请更换其他银行卡
            </md-notice-bar>
          </div>
          <div slot-scope="{ scene }" slot="footer">
            <div v-if="scene === 'choose' && !isCashierInitialed" class="cashier-loading">
              <md-activity-indicator :size="30" vertical>加载中...</md-activity-indicator>
            </div>
          </div>
          <div slot="payButton" style="display:flex;">
            <md-icon name="checked"></md-icon>发起支付
          </div>
          <div slot="scene" class="custom-scene">
            Custom Scene
          </div>
        </md-cashier>
        </div>
    </template>
    
    <script>
    import {Button, Icon, Cashier, Toast, NoticeBar, ActivityIndicator} from 'mand-mobile'
    
    export default {
      name: 'cashier-demo',
      components: {
        [Button.name]: Button,
        [Cashier.name]: Cashier,
        [Icon.name]: Icon,
        [NoticeBar.name]: NoticeBar,
        [ActivityIndicator.name]: ActivityIndicator,
      },
      data() {
        return {
          isCashierhow: false,
          isCashierInitialed: false,
          isCashierCaptcha: false,
          cashierAmount: '100.00',
          cashierResult: 'success',
          cashierResults: [
            {
              text: '支付成功',
              value: 'success',
            },
            {
              text: '支付失败',
              value: 'fail',
            },
          ],
          cashierChannels: [
            {
              img: 'https://pt-starimg.didistatic.com/static/starimg/img/rZBbFoIJEJ1546934427562.png',
              text: 'XX银行(1234)',
              desc: '当前银行维护中',
              value: '001',
              disabled: true,
              action: {
                text: '更换',
                handler: () => {
                  Toast.info('点击更换银行卡')
                },
              },
            },
          ],
        }
      },
      computed: {
        cashier() {
          return this.$refs.cashier
        },
      },
      methods: {
        doPay() {
          if (this.isCashierCaptcha) {
            this.cashier.next('captcha', {
              text: 'Verification code sent to 156 **** 8965',
              autoCountdown: false,
              countNormalText: 'Send Verification code',
              countActiveText: 'Retransmission after {$1}s',
              onSend: countdown => {
                console.log('[Mand Mobile] Send Captcha')
                this.sendCaptcha().then(() => {
                  countdown()
                })
              },
              onSubmit: code => {
                console.log(`[Mand Mobile] Send Submit ${code}`)
                this.checkCaptcha(code).then(res => {
                  if (res) {
                    this.createPay().then(() => {
                      this.cashier.next(this.cashierResult)
                    })
                  }
                })
              },
            })
          } else {
            this.createPay().then(() => {
              this.cashier.next(this.cashierResult, {
                actions: [
                  {
                    buttonText: '返回',
                    handler: () => {
                      this.cashier.next('choose')
                    },
                  },
                  {
                    buttonText: '重试',
                    handler: () => {
                      this.cashier.next('custom')
                    },
                  },
                ],
              })
            })
          }
        },
        // Create a pay request & check pay result
        createPay() {
          this.cashier.next('loading')
          return new Promise(resolve => {
            this.timer = setTimeout(() => {
              resolve()
            }, 3000)
          })
        },
        // Create a captcha sending request
        sendCaptcha() {
          return new Promise(resolve => {
            this.timer = setTimeout(() => {
              resolve()
            }, 200)
          })
        },
        // Create a captcha checking request
        checkCaptcha(code) {
          return new Promise(resolve => {
            this.timer = setTimeout(() => {
              resolve(!!code)
            }, 200)
          })
        },
        onCashierShow() {
          setTimeout(() => {
            this.isCashierInitialed = true
          }, 2000)
        },
        onCashierSelect(item) {
          console.log(`[Mand Mobile] Select ${JSON.stringify(item)}`)
        },
        onCashierPay(item) {
          console.log(`[Mand Mobile] Pay ${JSON.stringify(item)}`)
          this.doPay()
        },
        onCashierCancel() {
          // Abort pay request or checking request
          this.timer && clearTimeout(this.timer)
        },
      },
    }
    
    </script>
    
    <style lang="stylus">
    .md-example-child-cashier
      .md-field
        margin-bottom 30px
      .custom-scene
        min-height 300px
        display flex
        justify-content center
        align-items center
        font-size 32px
      .cashier-loading
        position absolute
        top 0
        left 0
        right 0
        bottom 0
        background rgba(255, 255, 255, 0.95)
        z-index 1400
        display flex
        align-items center
        justify-content center
    </style>
    
          

    API

    Cashier Props

    属性说明类型默认值备注
    v-model收银台是否显示Booleanfalse-
    channels支付渠道数据源Array<{text, value, icon, iconSvg, img, action}>[]icon可作为className或组件Iconname属性, iconSvg为是否使用svg图标, img为图标链接(与icon二选一), action为特殊动作回调
    channel-limit支付渠道超出限制数目时展示更多支付渠道按钮Number2-
    default-index默认选中支付渠道索引Number0-
    title收银台弹窗标题String支付-
    large-radius 2.4.0+选择器标题栏大圆角模式Booleanfalse-
    payment-title支付金额标题String支付金额(元)支持html fragment
    payment-amount支付金额String0.00支持html fragment
    payment-describe支付金额说明String-支持html fragment
    pay-button-text确认支付按钮文案String确认支付-
    pay-button-disabled禁用支付按钮Booleanfalse-
    more-button-text更多支付渠道按钮文案String更多支付方式支持html fragment

    Cashier Methods

    next(scene, option)

    进入收银台下一步

    参数说明类型默认值备注
    scene步骤场景标识String-choose(支付渠道选择)captcha(发送验证码)loading(支付中)success(支付成功)fail(支付失败)custom(自定义,使用插槽scene填充内容)
    option当前步骤场景配置Object属性如下所示-
    • captcha option

      属性说明类型默认值备注
      text发送验证码说明String--
      brief发送验证码简要描述String--
      maxlength验证码位数Number4若为-1则不限制输入长度
      count验证码重新发送倒计时Number60若为0则不显示重新发送
      autoCountdown是否自动开始倒计时,否则需手动调用countdownBooleantrue-
      countNormalText发送验证码正常状态文字String发送验证码-
      countActiveText发送验证码及倒计时按钮文案配置项String{$1}秒后重发-
      onSend验证码发送回调Function(countdown: Function)-countdown为开始倒计时方法
      onSubmit验证码提交回调Function(code: String)-code为输入的验证码
    • loading option

      属性说明类型默认值备注
      text支付中说明String支付结果查询中…支持html fragment
    • success option

      属性说明类型默认值备注
      text支付成功说明String支付成功支持html fragment
      buttonText按钮文案String我知道了支持html fragment
      handler按钮点击回调Function--
      actions按钮组Array<{buttonText, handler}>-有两个按钮时使用
    • fail option

      属性说明类型默认值备注
      text支付失败说明String支付失败,请稍后重试支持html fragment
      buttonText按钮文案String我知道了支持html fragment
      handler按钮点击回调Function--
      actions按钮组Array<{buttonText, handler}>-有两个按钮时使用

    Captcha Slots

    header

    头部内容scoped插槽

    <div slot-scope="{ scene }" slot="header">
      <md-notice-bar
        v-if="scene === 'choose'"
        mode="closable"
        icon="warn"
        type="warning"
      ></md-notice-bar>
    </div>

    底部内容scoped插槽

    channel

    支付渠道区域插槽,可用于添加支付渠道特殊操作,如添加银行卡

    payButton

    发起支付插槽

    scene

    自定义场景插槽,使用next('custom')打开

    Cashier Events

    @select(item: {text, value})

    支付渠道选中事件

    @pay(item: {text, value})

    支付渠道确认并发起支付事件

    @cancel()

    取消支付事件

    @show()

    收银台弹窗展示事件

    @hide()

    收银台弹窗隐藏事件