useQrCodePopup.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import { computed, reactive, readonly, ref } from 'vue'
  2. export interface QrCodePopupOptions {
  3. /**
  4. * Confirm callback for "Copy link" action.
  5. * Will be called when user clicks the primary button.
  6. */
  7. onCopyLink?: (link: string) => void
  8. }
  9. export interface QrCodePopupState {
  10. visible: boolean
  11. skillId: string
  12. skillName: string
  13. skillIcon: string
  14. price: number
  15. unit: string
  16. currencyAmount: number
  17. /** Generated QR code content/URL */
  18. qrcodeValue: string
  19. /** 当前激活的 tab:通用码 or 特定码 */
  20. activeTab: 'general' | 'specific'
  21. /** 特定码的内部状态:表单 or 预览二维码 */
  22. specificStep: 'form' | 'preview'
  23. /** 特定码 - 服务时长 */
  24. specificQty: number
  25. }
  26. const state = reactive<QrCodePopupState>({
  27. visible: false,
  28. skillId: '',
  29. skillName: '',
  30. skillIcon: '',
  31. price: 0,
  32. unit: '',
  33. currencyAmount: 0,
  34. qrcodeValue: '',
  35. activeTab: 'general',
  36. specificStep: 'form',
  37. specificQty: 1,
  38. })
  39. const copyHandler = ref<((link: string) => void) | null>(null)
  40. const formattedPrice = computed(() => state.price.toLocaleString())
  41. const currencyAmount = computed(() => state.currencyAmount.toLocaleString())
  42. const isGeneralTab = computed(() => state.activeTab === 'general')
  43. const isSpecificForm = computed(
  44. () => state.activeTab === 'specific' && state.specificStep === 'form',
  45. )
  46. const isSpecificPreview = computed(
  47. () => state.activeTab === 'specific' && state.specificStep === 'preview',
  48. )
  49. const open = (options: QrCodePopupOptions) => {
  50. state.qrcodeValue = '' // Reset on open
  51. // reset tab & specific state every time open
  52. state.activeTab = 'general'
  53. state.specificStep = 'form'
  54. state.specificQty = 1
  55. copyHandler.value = options.onCopyLink ?? null
  56. state.visible = true
  57. }
  58. const close = () => {
  59. state.visible = false
  60. }
  61. const switchTab = (tab: 'general' | 'specific') => {
  62. state.activeTab = tab
  63. // 切回通用码时,重置特定码到表单态
  64. if (tab === 'general') {
  65. state.specificStep = 'form'
  66. }
  67. }
  68. const resetSpecificStep = () => {
  69. state.specificStep = 'form'
  70. }
  71. const setSpecificQty = (value: number) => {
  72. const next = value || 1
  73. state.specificQty = next < 1 ? 1 : next
  74. }
  75. const generateSpecificQr = () => {
  76. state.specificStep = 'preview'
  77. }
  78. const updateQrcodeValue = (value: string) => {
  79. state.qrcodeValue = value
  80. }
  81. const updateCurrencyAmount = (value: number) => {
  82. state.currencyAmount = value
  83. }
  84. const updateSkillInfo = (info: { skillId: string, skillName: string, skillIcon: string, price: number, unit: string }) => {
  85. state.skillId = info.skillId
  86. state.skillName = info.skillName
  87. state.skillIcon = info.skillIcon
  88. state.price = info.price
  89. state.unit = info.unit
  90. }
  91. const handleCopyLink = () => {
  92. const handler = copyHandler.value
  93. if (handler) handler(state.qrcodeValue)
  94. }
  95. export const useQrCodePopup = () => {
  96. return {
  97. state: readonly(state),
  98. formattedPrice,
  99. currencyAmount,
  100. isGeneralTab,
  101. isSpecificForm,
  102. isSpecificPreview,
  103. open,
  104. close,
  105. switchTab,
  106. setSpecificQty,
  107. generateSpecificQr,
  108. handleCopyLink,
  109. updateQrcodeValue,
  110. updateCurrencyAmount,
  111. updateSkillInfo,
  112. resetSpecificStep,
  113. }
  114. }