cl-action-sheet.uvue 3.7 KB

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