This commit is contained in:
2025-03-25 23:25:59 +08:00
parent ddda5c1c68
commit 9579987fd9
61 changed files with 3902 additions and 1 deletions
@@ -0,0 +1,170 @@
<script lang="tsx">
import { onClickOutside, onKeyDown, syncRef, useDraggable, useVModel } from "@vueuse/core"
import type { PropType, RendererElement } from "vue"
import { Teleport, Transition, computed, defineComponent, provide, ref, renderSlot, watch } from "vue"
import { provideShow } from "@/components/hooks/show"
export default defineComponent({
name: "IDialog",
props: {
visible: {
type: Boolean,
default: () => false
},
class: {
type: String
},
mask: {
type: Boolean,
default: () => false
},
// 模态框
modal: {
type: Boolean,
default: () => false
},
header: {
type: [String, Boolean],
default: () => "提示"
},
closeOnEsc: {
type: Boolean,
default: () => true
},
closeButton: {
type: Boolean,
default: () => true
},
cache: {
// 维持窗口状态
type: Boolean,
default: () => true
},
lazy: {
type: Boolean,
default: () => false
},
drag: {
type: String as PropType<"header" | "all" | "none">,
default: () => "header"
},
appendTo: {
type: [Object, String] as PropType<string | RendererElement | null | undefined>,
default: "body"
},
scale: {
type: Number,
default: 1
}
},
emits: ["close", "update:visible"],
setup(props, { emit, slots }) {
const dialogRef = ref<HTMLElement | null>(null)
const headerRef = ref<HTMLElement | null>(null)
const visible = ref(props.visible)
provideShow(visible)
provide("dialogRef", dialogRef)
const loaded = ref(!props.lazy)
const stopLoadWatch = watch(visible, val => {
if (val) {
loaded.value = true
stopLoadWatch()
}
})
syncRef(useVModel(props, "visible"), visible, { direction: "both" })
onClickOutside(dialogRef, () => {
if (props.modal) {
close()
}
})
function close() {
visible.value = false
emit("close")
}
onKeyDown(
"Escape",
() => {
if (visible.value && props.closeOnEsc) {
close()
}
},
{ target: document }
)
const init = ref(true)
const { x, y, style } = useDraggable(
computed(() => {
switch (props.drag) {
case "header":
return headerRef.value
case "all":
return dialogRef.value
case "none":
default:
return null
}
}),
{
initialValue: { x: 0, y: 0 }
}
)
const isFixed = computed(() => x.value + y.value > 0)
const appendToRef = computed(() => {
return props.appendTo || "body"
})
return () => {
return (
<Teleport to={appendToRef.value}>
<Transition name="dialog" mode="out-in">
{loaded.value && (props.cache || visible.value) && (
<div v-show={visible.value} class={["dialog-mask w-full h-full fixed top-0 left-0 z-999 flex justify-center items-center "].concat(props.mask ? "bg-hex-000 bg-opacity-66" : "")} style={`transform: scale(${props.scale});${x.value == 0 && y.value == 0 ?"transform-origin:center":"transform-origin:left top"}`}>
<div ref={dialogRef} class={["h-auto border-1 border-hex-2b2b2b border-solid shadow-sm round-1 dialog min-w-48", isFixed.value ? "fixed" : "", props.class]} style={`top:${y.value/props.scale}px;left:${x.value/props.scale}px`}>
{props.header && (
<div ref={headerRef} class="flex h-4 px-1 leading-4 z-9999 justify-center relative app-header py-2px">
<div class="text-xs text-shadow ">{props.header}</div>
{props.closeButton && (
<div class="flex top-0 right-0 bottom-0 items-center absolute">
<div onClick={close} class="cursor-pointer flex h-4 text-center w-4 items-center close-icon"></div>
</div>
)}
</div>
)}
<div class="bg-hex-000 bg-opacity-80 text-white text-xs">{renderSlot(slots, "default")}</div>
</div>
</div>
)}
</Transition>
</Teleport>
)
}
}
})
</script>
<style lang="scss" scoped>
.dialog-enter-active {
animation: fade-in 400ms;
.dialog {
animation: zoom-in 400ms;
}
}
.dialog-leave-active {
animation: fade-in reverse 400ms;
.dialog {
animation: zoom-out 400ms;
}
}
</style>