133 lines
2.8 KiB
Vue
133 lines
2.8 KiB
Vue
<template>
|
|
<t-popup v-model="visible" placement="bottom">
|
|
<div class="bottom-popup" :style="{ height: props.height }">
|
|
<div v-if="hasHeader" class="header">
|
|
<div class="button left">
|
|
<slot name="leftButton" :on-click="emitLeftButtonClick">
|
|
<t-button v-if="leftButtonText" variant="text" size="small" @click="emitLeftButtonClick">
|
|
<span v-text="leftButtonText" />
|
|
</t-button>
|
|
</slot>
|
|
</div>
|
|
<div class="title text-bold">
|
|
<slot name="title">
|
|
<span v-text="title" />
|
|
</slot>
|
|
</div>
|
|
<div class="button right">
|
|
<slot name="rightButton" :on-click="emitRightButtonClick">
|
|
<t-button v-if="rightButtonText" theme="primary" variant="text" size="small" @click="emitRightButtonClick">
|
|
<span v-text="rightButtonText" />
|
|
</t-button>
|
|
</slot>
|
|
</div>
|
|
</div>
|
|
<div class="body">
|
|
<slot />
|
|
</div>
|
|
</div>
|
|
</t-popup>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
defineOptions({
|
|
name: "BottomPopup"
|
|
});
|
|
|
|
export interface BottomPopupProps {
|
|
modelValue?: boolean;
|
|
leftButtonText?: string;
|
|
rightButtonText?: string;
|
|
height?: string | number;
|
|
title?: string;
|
|
}
|
|
|
|
export interface BottomPopupSlots {
|
|
default?: () => unknown;
|
|
title?: () => unknown;
|
|
leftButton?: (props: { onClick: () => void }) => unknown;
|
|
rightButton?: (props: { onClick: () => void }) => unknown;
|
|
}
|
|
|
|
const props = withDefaults(defineProps<BottomPopupProps>(), {
|
|
modelValue: false,
|
|
leftButtonText: "取消",
|
|
rightButtonText: "确定",
|
|
height: "auto",
|
|
title: ""
|
|
});
|
|
|
|
const emit = defineEmits<{
|
|
"update:modelValue": [value: boolean];
|
|
leftButtonClick: [];
|
|
rightButtonClick: [];
|
|
}>();
|
|
|
|
const slots = useSlots() as BottomPopupSlots;
|
|
const visible = computed({
|
|
get: () => props.modelValue,
|
|
set: value => emit("update:modelValue", value)
|
|
});
|
|
const hasHeader = computed<boolean>(() => Boolean(
|
|
props.title
|
|
|| props.leftButtonText
|
|
|| props.rightButtonText
|
|
|| slots.title
|
|
|| slots.leftButton
|
|
|| slots.rightButton
|
|
));
|
|
|
|
function emitLeftButtonClick(): void {
|
|
emit("leftButtonClick");
|
|
}
|
|
|
|
function emitRightButtonClick(): void {
|
|
emit("rightButtonClick");
|
|
}
|
|
|
|
defineSlots<BottomPopupSlots>();
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
.bottom-popup {
|
|
display: flex;
|
|
overflow: hidden;
|
|
padding-top: 1rem;
|
|
background: var(--td-bg-color-container, #fff);
|
|
border-radius: 1.25rem 1.25rem 0 0;
|
|
flex-direction: column;
|
|
|
|
.header {
|
|
flex: none;
|
|
padding: 0 1rem;
|
|
display: grid;
|
|
align-items: center;
|
|
grid-template-columns: 4rem 1fr 4rem;
|
|
}
|
|
|
|
.button {
|
|
min-width: 0;
|
|
display: flex;
|
|
|
|
&.right {
|
|
justify-content: flex-end;
|
|
}
|
|
}
|
|
|
|
.title {
|
|
margin: 0;
|
|
overflow: hidden;
|
|
text-align: center;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.body {
|
|
flex: 1;
|
|
padding: 0 0 env(safe-area-inset-bottom, 0);
|
|
overflow: auto;
|
|
min-height: 0;
|
|
}
|
|
}
|
|
</style>
|