wd-action-sheet.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. <template>
  2. <view>
  3. <wd-popup
  4. custom-class="wd-action-sheet__popup"
  5. :custom-style="`${(actions && actions.length) || (panels && panels.length) ? 'background: transparent;' : ''}`"
  6. v-model="showPopup"
  7. :duration="duration"
  8. position="bottom"
  9. :close-on-click-modal="closeOnClickModal"
  10. :safe-area-inset-bottom="safeAreaInsetBottom"
  11. :lazy-render="lazyRender"
  12. :root-portal="rootPortal"
  13. @enter="handleOpen"
  14. @close="close"
  15. @after-enter="handleOpened"
  16. @after-leave="handleClosed"
  17. @click-modal="handleClickModal"
  18. :z-index="zIndex"
  19. >
  20. <view
  21. :class="`wd-action-sheet ${customClass}`"
  22. :style="`${
  23. (actions && actions.length) || (panels && panels.length)
  24. ? 'margin: 0 10px calc(var(--window-bottom) + 10px) 10px; border-radius: 16px;'
  25. : 'margin-bottom: var(--window-bottom);'
  26. } ${customStyle}`"
  27. >
  28. <view v-if="title" :class="`wd-action-sheet__header ${customHeaderClass}`">
  29. {{ title }}
  30. <wd-icon custom-class="wd-action-sheet__close" name="add" @click="close" />
  31. </view>
  32. <view class="wd-action-sheet__actions" v-if="actions && actions.length">
  33. <button
  34. v-for="(action, rowIndex) in actions"
  35. :key="rowIndex"
  36. :class="`wd-action-sheet__action ${action.disabled ? 'wd-action-sheet__action--disabled' : ''} ${
  37. action.loading ? 'wd-action-sheet__action--loading' : ''
  38. }`"
  39. :style="`color: ${action.color}`"
  40. @click="select(rowIndex, 'action')"
  41. >
  42. <wd-loading custom-class="`wd-action-sheet__action-loading" v-if="action.loading" />
  43. <view v-else class="wd-action-sheet__name">{{ action.name }}</view>
  44. <view v-if="!action.loading && action.subname" class="wd-action-sheet__subname">{{ action.subname }}</view>
  45. </button>
  46. </view>
  47. <view v-if="formatPanels && formatPanels.length">
  48. <view v-for="(panel, rowIndex) in formatPanels" :key="rowIndex" class="wd-action-sheet__panels">
  49. <view class="wd-action-sheet__panels-content">
  50. <view v-for="(col, colIndex) in panel" :key="colIndex" class="wd-action-sheet__panel" @click="select(rowIndex, 'panels', colIndex)">
  51. <image class="wd-action-sheet__panel-img" :src="(col as any).iconUrl" />
  52. <view class="wd-action-sheet__panel-title">{{ (col as any).title }}</view>
  53. </view>
  54. </view>
  55. </view>
  56. </view>
  57. <slot />
  58. <button v-if="cancelText" class="wd-action-sheet__cancel" @click="handleCancel">{{ cancelText }}</button>
  59. </view>
  60. </wd-popup>
  61. </view>
  62. </template>
  63. <script lang="ts">
  64. export default {
  65. name: 'wd-action-sheet',
  66. options: {
  67. addGlobalClass: true,
  68. virtualHost: true,
  69. styleIsolation: 'shared'
  70. }
  71. }
  72. </script>
  73. <script lang="ts" setup>
  74. import wdPopup from '../wd-popup/wd-popup.vue'
  75. import wdIcon from '../wd-icon/wd-icon.vue'
  76. import wdLoading from '../wd-loading/wd-loading.vue'
  77. import { watch, ref } from 'vue'
  78. import { actionSheetProps, type Panel } from './types'
  79. import { isArray } from '../common/util'
  80. const props = defineProps(actionSheetProps)
  81. const emit = defineEmits(['select', 'click-modal', 'cancel', 'closed', 'close', 'open', 'opened', 'update:modelValue'])
  82. const formatPanels = ref<Array<Panel> | Array<Panel[]>>([])
  83. const showPopup = ref<boolean>(false)
  84. watch(() => props.panels, computedValue, { deep: true, immediate: true })
  85. watch(
  86. () => props.modelValue,
  87. (newValue) => {
  88. showPopup.value = newValue
  89. },
  90. { deep: true, immediate: true }
  91. )
  92. function isPanelArray() {
  93. return props.panels.length && !isArray(props.panels[0])
  94. }
  95. function computedValue() {
  96. formatPanels.value = isPanelArray() ? [props.panels as Panel[]] : (props.panels as Panel[][])
  97. }
  98. function select(rowIndex: number, type: 'action' | 'panels', colIndex?: number) {
  99. if (type === 'action') {
  100. if (props.actions[rowIndex].disabled || props.actions[rowIndex].loading) {
  101. return
  102. }
  103. emit('select', {
  104. item: props.actions[rowIndex],
  105. index: rowIndex
  106. })
  107. } else if (isPanelArray()) {
  108. emit('select', {
  109. item: props.panels[Number(colIndex)],
  110. index: colIndex
  111. })
  112. } else {
  113. emit('select', {
  114. item: (props.panels as Panel[][])[rowIndex][Number(colIndex)],
  115. rowIndex,
  116. colIndex
  117. })
  118. }
  119. if (props.closeOnClickAction) {
  120. close()
  121. }
  122. }
  123. function handleClickModal() {
  124. emit('click-modal')
  125. }
  126. function handleCancel() {
  127. emit('cancel')
  128. close()
  129. }
  130. function close() {
  131. emit('update:modelValue', false)
  132. emit('close')
  133. }
  134. function handleOpen() {
  135. emit('open')
  136. }
  137. function handleOpened() {
  138. emit('opened')
  139. }
  140. function handleClosed() {
  141. emit('closed')
  142. }
  143. </script>
  144. <style lang="scss" scoped>
  145. @import './index.scss';
  146. </style>