cl-action-sheet.uvue 3.9 KB

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