Files
TeyvatGuide/src/components/func/dialog.vue
2024-12-13 16:15:01 +08:00

296 lines
6.8 KiB
Vue

<template>
<transition name="func-dialog-outer">
<div v-show="show || showOuter" class="dialog-overlay" @click.self.prevent="handleOuter">
<transition name="func-dialog-inner">
<div v-show="showInner" class="dialog-box">
<div class="dialog-title">{{ data.title }}</div>
<div
v-show="data?.text !== '' && data.mode === 'check'"
class="dialog-subtitle"
:title="data.text"
>
{{ data.text }}
</div>
<div v-show="data?.text !== '' && data.mode === 'input'" class="dialog-input">
<div class="dialog-input-label">{{ data.text }}</div>
<input
v-model="inputDefault"
class="dialog-input-box"
ref="inputRef"
@keydown.enter="handleConfirm"
/>
</div>
<div class="dialog-btn-box">
<button class="dialog-btn no-btn" @click="handleCancel">取消</button>
<button class="dialog-btn ok-btn" @click="handleConfirm">确定</button>
</div>
</div>
</transition>
</div>
</transition>
</template>
<script lang="ts" setup>
import { computed, onMounted, reactive, ref, useTemplateRef, watch } from "vue";
import type { DialogCheckParams, DialogInputParams, DialogParams } from "./dialog.js";
const defaultProp: DialogParams = { title: "", text: "", mode: "check", otcancel: false };
const props = defineProps<DialogParams>();
// 组件参数
const data = reactive<DialogParams>(defaultProp);
const show = ref<boolean>(false);
const showOuter = ref<boolean>(false);
const showInner = ref<boolean>(false);
const dialogVal = ref<boolean | string | undefined>();
const inputDefault = ref<string>("");
const inputEl = useTemplateRef<HTMLInputElement>("inputRef");
const checkVal = computed<boolean | undefined>(() => {
if (typeof dialogVal.value === "string") return dialogVal.value !== "";
return dialogVal.value;
});
const inputVal = computed<string | false | undefined>(() => {
if (typeof dialogVal.value === "string") return dialogVal.value;
if (dialogVal.value === undefined) return undefined;
return false;
});
watch(
() => show.value,
() => {
if (show.value) {
showOuter.value = true;
setTimeout(() => (showInner.value = true), 100);
return;
}
setTimeout(() => (showInner.value = false), 100);
setTimeout(() => (showOuter.value = false), 300);
},
);
onMounted(async () => {
if (props.mode === "input") {
const param: DialogInputParams = {
mode: "input",
title: props.title,
text: props.text,
input: props.input,
otcancel: props.otcancel,
};
await displayInputBox(param);
} else {
const param: DialogCheckParams = {
mode: "check",
title: props.title,
text: props.text,
otcancel: props.otcancel,
};
await displayCheckBox(param);
}
});
async function displayCheckBox(params: DialogCheckParams): Promise<boolean | undefined> {
data.title = params.title;
data.text = params.text ?? "";
data.mode = "check";
data.otcancel = params.otcancel ?? true;
show.value = true;
return await new Promise<boolean | undefined>((resolve) => {
watch(
() => show.value,
() => setTimeout(() => resolve(checkVal.value), 500),
);
});
}
async function displayInputBox(params: DialogInputParams): Promise<string | false | undefined> {
data.title = params.title;
data.text = params.text ?? "";
data.mode = "input";
inputDefault.value = params.input ?? "";
data.otcancel = params.otcancel ?? true;
show.value = true;
return await new Promise<string | false | undefined>((resolve) => {
setTimeout(() => inputEl.value?.focus(), 100);
watch(
() => show.value,
() => setTimeout(() => resolve(inputVal.value), 500),
);
});
}
// 确认
function handleConfirm(): void {
if (data.mode === "input") {
dialogVal.value = inputDefault.value;
inputDefault.value = "";
} else {
dialogVal.value = true;
}
show.value = false;
}
// 取消
function handleCancel(): void {
dialogVal.value = false;
show.value = false;
}
// 点击外部事件
function handleOuter(): void {
if (data.otcancel) {
dialogVal.value = undefined;
show.value = false;
}
}
defineExpose({ displayInputBox, displayCheckBox });
</script>
<style scoped>
.func-dialog-outer-enter-active,
.func-dialog-outer-leave-active,
.func-dialog-inner-enter-active {
transition: all 0.3s;
}
.func-dialog-inner-leave-active {
transition: all 0.5s ease-in-out;
}
.func-dialog-inner-enter-from {
opacity: 0;
transform: scale(1.5);
}
.func-dialog-inner-enter-to,
.func-dialog-inner-leave-from {
opacity: 1;
transform: scale(1);
}
.func-dialog-outer-enter-to,
.func-dialog-outer-leave-from {
opacity: 1;
}
.func-dialog-outer-enter-from,
.func-dialog-outer-leave-to {
opacity: 0;
}
.func-dialog-inner-leave-to {
opacity: 0;
transform: scale(0);
}
.dialog-overlay {
position: fixed;
z-index: 100;
top: 0;
left: 0;
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
/* 颜色变量 */
--dialog-title: var(--tgc-dark-7);
--dialog-bg: var(--tgc-white-1);
}
/* 深色模式 */
.dark .dialog-overlay {
--dialog-title: var(--tgc-white-1);
--dialog-bg: var(--tgc-dark-7);
}
.dialog-box {
display: flex;
width: 520px;
height: 240px;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 10px;
border: 1px solid var(--common-shadow-1);
border-radius: 15px;
background: var(--dialog-bg);
box-shadow: 0 0 10px var(--common-shadow-t-1);
color: var(--tgc-yellow-3);
}
.dialog-title {
width: 100%;
border-bottom: 1px solid var(--dialog-title);
color: var(--dialog-title);
font-family: var(--font-title);
font-size: 30px;
text-align: center;
}
.dialog-subtitle {
overflow: hidden;
width: 100%;
font-family: var(--font-text);
font-size: 20px;
text-align: center;
text-overflow: ellipsis;
white-space: normal;
word-break: break-all;
}
.dialog-input {
display: flex;
width: 100%;
align-items: center;
justify-content: center;
font-family: var(--font-text);
font-size: 16px;
gap: 10px;
}
.dialog-input-box {
width: 50%;
height: 100%;
padding: 5px;
border: 1px solid var(--dialog-title);
border-radius: 5px;
background: inherit;
color: var(--dialog-title);
}
.dialog-btn-box {
display: flex;
width: 100%;
align-items: flex-end;
justify-content: space-around;
}
.dialog-btn {
position: relative;
display: flex;
width: 180px;
height: 60px;
align-items: center;
justify-content: center;
border-radius: 15px;
cursor: pointer;
font-family: var(--font-title);
font-size: 20px;
}
.no-btn {
border: 1px solid var(--tgc-yellow-1);
}
.ok-btn {
background: var(--dialog-title);
}
</style>