mirror of https://github.com/halo-dev/halo
parent
3853d55fd1
commit
c0e6267ccc
|
@ -0,0 +1,21 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { VButton } from "@/components/base/button";
|
||||||
|
import { VModal } from "@/components/base/modal";
|
||||||
|
|
||||||
|
function initState() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Story title="Modal" :init-state="initState">
|
||||||
|
<template #default="{ state }">
|
||||||
|
<VButton type="secondary" @click="state.visible = true">打开</VButton>
|
||||||
|
<VModal v-model:visible="state.visible" title="测试">
|
||||||
|
Hello World
|
||||||
|
<VButton type="secondary" @click="state.visible = false">关闭</VButton>
|
||||||
|
</VModal>
|
||||||
|
</template>
|
||||||
|
</Story>
|
||||||
|
</template>
|
|
@ -0,0 +1,161 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { VButton } from "../button";
|
||||||
|
import { computed } from "vue";
|
||||||
|
import { IconClose } from "@/core/icons";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
default: 500,
|
||||||
|
},
|
||||||
|
fullscreen: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:visible", "close"]);
|
||||||
|
|
||||||
|
const wrapperClasses = computed(() => {
|
||||||
|
return {
|
||||||
|
"modal-wrapper-fullscreen": props.fullscreen,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const contentStyles = computed(() => {
|
||||||
|
return {
|
||||||
|
maxWidth: props.width + "px",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
emit("update:visible", false);
|
||||||
|
emit("close");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<transition
|
||||||
|
enter-active-class="ease-out"
|
||||||
|
enter-from-class="opacity-0"
|
||||||
|
enter-to-class="opacity-100"
|
||||||
|
leave-active-class="ease-in"
|
||||||
|
leave-from-class="opacity-100"
|
||||||
|
leave-to-class="opacity-0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-show="visible"
|
||||||
|
:class="wrapperClasses"
|
||||||
|
aria-modal="true"
|
||||||
|
class="modal-wrapper transform transition-all duration-200"
|
||||||
|
role="dialog"
|
||||||
|
tabindex="0"
|
||||||
|
@keyup.esc="handleClose()"
|
||||||
|
>
|
||||||
|
<div class="modal-layer" @click="handleClose()" />
|
||||||
|
<div :style="contentStyles" class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<div class="modal-header-title">{{ title }}</div>
|
||||||
|
<div class="modal-header-actions">
|
||||||
|
<div class="modal-header-action" @click="handleClose()">
|
||||||
|
<IconClose />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<slot name="footer">
|
||||||
|
<VButton @click="handleClose">关闭</VButton>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.modal-wrapper {
|
||||||
|
@apply fixed;
|
||||||
|
@apply top-0 left-0;
|
||||||
|
@apply w-full h-full;
|
||||||
|
@apply flex flex-row;
|
||||||
|
@apply items-center justify-center;
|
||||||
|
z-index: 99999;
|
||||||
|
|
||||||
|
.modal-layer {
|
||||||
|
@apply flex-none;
|
||||||
|
@apply absolute;
|
||||||
|
@apply top-0 left-0;
|
||||||
|
@apply w-full h-full;
|
||||||
|
background: #9e9eaa;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
@apply flex;
|
||||||
|
@apply flex-col;
|
||||||
|
@apply relative;
|
||||||
|
@apply bg-white;
|
||||||
|
@apply items-stretch;
|
||||||
|
@apply shadow-xl;
|
||||||
|
width: calc(100vw - 20px);
|
||||||
|
max-height: calc(100vh - 20px);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
@apply flex;
|
||||||
|
@apply justify-between;
|
||||||
|
@apply border-b;
|
||||||
|
|
||||||
|
.modal-header-title {
|
||||||
|
@apply self-center;
|
||||||
|
@apply text-base;
|
||||||
|
@apply font-bold;
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header-actions {
|
||||||
|
@apply self-center;
|
||||||
|
@apply h-full;
|
||||||
|
.modal-header-action {
|
||||||
|
@apply cursor-pointer;
|
||||||
|
padding: 12px 16px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
@apply bg-gray-100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
@apply overflow-x-hidden overflow-y-auto;
|
||||||
|
word-wrap: break-word;
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
@apply border-t;
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.modal-wrapper-fullscreen {
|
||||||
|
.modal-content {
|
||||||
|
width: 100vw !important;
|
||||||
|
max-width: 100vw !important;
|
||||||
|
height: 100vh !important;
|
||||||
|
max-height: 100vh !important;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { VModal } from "../index";
|
||||||
|
|
||||||
|
describe("Modal", () => {
|
||||||
|
it("should render", () => {
|
||||||
|
expect(VModal).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as VModal } from "./Modal.vue";
|
|
@ -26,6 +26,9 @@ import IconEye from "~icons/ri/eye-line";
|
||||||
import IconFolder from "~icons/ri/folder-2-line";
|
import IconFolder from "~icons/ri/folder-2-line";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import IconMore from "~icons/ri/more-line";
|
import IconMore from "~icons/ri/more-line";
|
||||||
|
// @ts-ignore
|
||||||
|
import IconClose from "~icons/ri/close-line";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
IconDashboard,
|
IconDashboard,
|
||||||
IconArrowRight,
|
IconArrowRight,
|
||||||
|
@ -41,4 +44,5 @@ export {
|
||||||
IconEye,
|
IconEye,
|
||||||
IconFolder,
|
IconFolder,
|
||||||
IconMore,
|
IconMore,
|
||||||
|
IconClose,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,22 @@
|
||||||
<template>
|
<template>
|
||||||
<FilledLayout> </FilledLayout>
|
<FilledLayout>
|
||||||
|
<VModal v-model:visible="visible" title="登录">
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<div>
|
||||||
|
<VButton type="secondary" @click="visible = false">关闭</VButton>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<VButton type="secondary" @click="visible = true">打开</VButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</VModal>
|
||||||
|
<VButton type="secondary" @click="visible = true">打开</VButton>
|
||||||
|
</FilledLayout>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { FilledLayout } from "@/layouts";
|
import { FilledLayout } from "@/layouts";
|
||||||
|
import { VModal } from "@/components/base/modal";
|
||||||
|
import { VButton } from "@/components/base/button";
|
||||||
|
import { ref } from "vue";
|
||||||
|
const visible = ref(false);
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in New Issue