180 lines
3.7 KiB
Plaintext
180 lines
3.7 KiB
Plaintext
<template>
|
|
<cl-popup
|
|
v-model="visible"
|
|
:show-close="false"
|
|
:swipe-close-threshold="50"
|
|
:pt="{
|
|
inner: {
|
|
className: parseClass([[isDark, '!bg-surface-700', '!bg-surface-100']])
|
|
}
|
|
}"
|
|
:mask-closable="config.maskClosable"
|
|
:title="config.title"
|
|
>
|
|
<view class="cl-action-sheet" :class="[pt.className]">
|
|
<slot name="prepend"></slot>
|
|
|
|
<view class="cl-action-sheet__description" v-if="config.description != ''">
|
|
<cl-text
|
|
:pt="{
|
|
className: '!text-surface-400 !text-md text-center'
|
|
}"
|
|
>{{ config.description }}</cl-text
|
|
>
|
|
</view>
|
|
|
|
<view class="cl-action-sheet__list" :class="[pt.list?.className]">
|
|
<view
|
|
class="cl-action-sheet__item"
|
|
:class="[`${isDark ? '!bg-surface-800' : 'bg-white'}`, pt.item?.className]"
|
|
v-for="(item, index) in config.list"
|
|
:key="index"
|
|
:hover-class="`${isDark ? '!bg-surface-900' : '!bg-surface-50'}`"
|
|
:hover-stay-time="250"
|
|
@tap="onItemTap(item)"
|
|
>
|
|
<slot name="item" :item="item">
|
|
<cl-icon
|
|
:name="item.icon"
|
|
:pt="{
|
|
className: 'mr-2'
|
|
}"
|
|
:color="item.color"
|
|
v-if="item.icon != null"
|
|
></cl-icon>
|
|
<cl-text :color="item.color">{{ item.label }}</cl-text>
|
|
</slot>
|
|
</view>
|
|
</view>
|
|
|
|
<slot name="append"></slot>
|
|
</view>
|
|
</cl-popup>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, reactive, computed } from "vue";
|
|
import type { ClActionSheetItem, ClActionSheetOptions, PassThroughProps } from "../../types";
|
|
import { t } from "@/locale";
|
|
import { isDark, parseClass, parsePt } from "@/cool";
|
|
|
|
defineOptions({
|
|
name: "cl-action-sheet"
|
|
});
|
|
|
|
defineSlots<{
|
|
prepend(): any;
|
|
append(): any;
|
|
item(props: { item: ClActionSheetItem }): any;
|
|
}>();
|
|
|
|
// 组件属性定义
|
|
const props = defineProps({
|
|
// 透传样式配置
|
|
pt: {
|
|
type: Object,
|
|
default: () => ({})
|
|
}
|
|
});
|
|
|
|
// 透传样式类型定义
|
|
type PassThrough = {
|
|
className?: string; // 根元素类名
|
|
item?: PassThroughProps; // 列表项样式
|
|
list?: PassThroughProps; // 列表样式
|
|
};
|
|
|
|
// 解析透传样式配置
|
|
const pt = computed(() => parsePt<PassThrough>(props.pt));
|
|
|
|
// 控制弹窗显示状态
|
|
const visible = ref(false);
|
|
|
|
// 操作表配置数据
|
|
const config = reactive<ClActionSheetOptions>({
|
|
title: "", // 标题
|
|
list: [] // 操作列表
|
|
});
|
|
|
|
/**
|
|
* 关闭操作表
|
|
* 设置visible为false隐藏弹窗
|
|
*/
|
|
function close() {
|
|
visible.value = false;
|
|
}
|
|
|
|
/**
|
|
* 打开操作表
|
|
* @param options 操作表配置选项
|
|
*/
|
|
function open(options: ClActionSheetOptions) {
|
|
// 显示弹窗
|
|
visible.value = true;
|
|
|
|
// 更新标题
|
|
config.title = options.title;
|
|
|
|
// 更新描述
|
|
config.description = options.description ?? "";
|
|
|
|
// 更新操作列表
|
|
config.list = options.list;
|
|
|
|
// 取消按钮文本
|
|
config.cancelText = options.cancelText ?? t("取消");
|
|
|
|
// 是否显示取消按钮
|
|
config.showCancel = options.showCancel ?? true;
|
|
|
|
// 是否可以点击遮罩关闭
|
|
config.maskClosable = options.maskClosable ?? true;
|
|
|
|
// 如果需要显示取消按钮,添加到列表末尾
|
|
if (config.showCancel!) {
|
|
config.list.push({
|
|
label: config.cancelText!,
|
|
callback() {
|
|
close();
|
|
}
|
|
} as ClActionSheetItem);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 点击列表项事件处理
|
|
* @param item 被点击的操作项
|
|
*/
|
|
function onItemTap(item: ClActionSheetItem) {
|
|
// 如果存在回调函数则执行
|
|
if (item.callback != null) {
|
|
item.callback!();
|
|
}
|
|
}
|
|
|
|
// 暴露组件方法供外部调用
|
|
defineExpose({
|
|
open,
|
|
close
|
|
});
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.cl-action-sheet {
|
|
&__description {
|
|
@apply flex flex-row items-center justify-center;
|
|
margin-bottom: 30rpx;
|
|
}
|
|
|
|
&__list {
|
|
padding: 0 20rpx;
|
|
}
|
|
|
|
&__item {
|
|
@apply flex flex-row items-center justify-center rounded-lg;
|
|
padding: 20rpx;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
}
|
|
</style>
|