cl-action-sheet.uvue 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. <template>
  2. <cl-popup
  3. v-model="visible"
  4. :show-close="false"
  5. :swipe-close-threshold="50"
  6. :pt="{
  7. inner: {
  8. className: parseClass([[isDark, '!bg-surface-700', '!bg-surface-100']])
  9. }
  10. }"
  11. :mask-closable="config.maskClosable"
  12. :title="config.title"
  13. >
  14. <view class="cl-action-sheet" :class="[pt.className]">
  15. <slot name="prepend"></slot>
  16. <view class="cl-action-sheet__description" v-if="config.description != ''">
  17. <text class="text-surface-400 text-md">{{ config.description }}</text>
  18. </view>
  19. <view class="cl-action-sheet__list">
  20. <view
  21. class="cl-action-sheet__item"
  22. :class="[`${isDark ? '!bg-surface-800' : 'bg-white'}`, pt.item?.className]"
  23. v-for="(item, index) in config.list"
  24. :key="index"
  25. :hover-class="`${isDark ? '!bg-surface-900' : '!bg-surface-50'}`"
  26. :hover-stay-time="100"
  27. @tap="onItemTap(item)"
  28. >
  29. <slot name="item" :item="item">
  30. <cl-icon
  31. :name="item.icon"
  32. :pt="{
  33. className: 'mr-2'
  34. }"
  35. :color="item.color"
  36. v-if="item.icon != null"
  37. ></cl-icon>
  38. <cl-text :color="item.color">{{ item.label }}</cl-text>
  39. </slot>
  40. </view>
  41. </view>
  42. <slot name="append"></slot>
  43. </view>
  44. </cl-popup>
  45. </template>
  46. <script setup lang="ts">
  47. import { ref, reactive, computed } from "vue";
  48. import type { ClActionSheetItem, ClActionSheetOptions, PassThroughProps } from "../../types";
  49. import { t } from "@/locale";
  50. import { isDark, parseClass, parsePt } from "@/cool";
  51. defineOptions({
  52. name: "cl-action-sheet"
  53. });
  54. defineSlots<{
  55. prepend(): any;
  56. append(): any;
  57. item(props: { item: ClActionSheetItem }): any;
  58. }>();
  59. // 组件属性定义
  60. const props = defineProps({
  61. // 透传样式配置
  62. pt: {
  63. type: Object,
  64. default: () => ({})
  65. }
  66. });
  67. // 透传样式类型定义
  68. type PassThrough = {
  69. className?: string; // 根元素类名
  70. item?: PassThroughProps; // 列表项样式
  71. };
  72. // 解析透传样式配置
  73. const pt = computed(() => parsePt<PassThrough>(props.pt));
  74. // 控制弹窗显示状态
  75. const visible = ref(false);
  76. // 操作表配置数据
  77. const config = reactive<ClActionSheetOptions>({
  78. title: "", // 标题
  79. list: [] // 操作列表
  80. });
  81. /**
  82. * 关闭操作表
  83. * 设置visible为false隐藏弹窗
  84. */
  85. function close() {
  86. visible.value = false;
  87. }
  88. /**
  89. * 打开操作表
  90. * @param options 操作表配置选项
  91. */
  92. function open(options: ClActionSheetOptions) {
  93. // 显示弹窗
  94. visible.value = true;
  95. // 更新标题
  96. config.title = options.title;
  97. // 更新描述
  98. config.description = options.description ?? "";
  99. // 更新操作列表
  100. config.list = options.list;
  101. // 取消按钮文本
  102. config.cancelText = options.cancelText ?? t("取消");
  103. // 是否显示取消按钮
  104. config.showCancel = options.showCancel ?? true;
  105. // 是否可以点击遮罩关闭
  106. config.maskClosable = options.maskClosable ?? true;
  107. // 如果需要显示取消按钮,添加到列表末尾
  108. if (config.showCancel!) {
  109. config.list.push({
  110. label: config.cancelText!,
  111. callback() {
  112. close();
  113. }
  114. } as ClActionSheetItem);
  115. }
  116. }
  117. /**
  118. * 点击列表项事件处理
  119. * @param item 被点击的操作项
  120. */
  121. function onItemTap(item: ClActionSheetItem) {
  122. // 如果存在回调函数则执行
  123. if (item.callback != null) {
  124. item.callback!();
  125. }
  126. }
  127. // 暴露组件方法供外部调用
  128. defineExpose({
  129. open,
  130. close
  131. });
  132. </script>
  133. <style scoped lang="scss">
  134. .cl-action-sheet {
  135. &__description {
  136. @apply flex flex-row items-center justify-center;
  137. margin-bottom: 30rpx;
  138. }
  139. &__list {
  140. padding: 0 20rpx;
  141. }
  142. &__item {
  143. @apply flex flex-row items-center justify-center rounded-lg;
  144. padding: 20rpx;
  145. margin-bottom: 20rpx;
  146. }
  147. }
  148. </style>