Skip to content

Commit fd5707a

Browse files
committed
fix: optimize Modal related component
1 parent fb86e00 commit fd5707a

20 files changed

+316
-985
lines changed

apps/client/components/Navbar.vue

+8-8
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,17 @@
7878
</div>
7979
</div>
8080
</header>
81-
<MainMessageBox
82-
v-model:isShowModal="isShowModal"
83-
title="提示"
84-
content="是否确认退出登录?"
85-
@confirm="signOut()"
86-
/>
81+
8782
<UserMenu
8883
v-model:open="isOpenUserMenu"
8984
@logout="handleLogout"
90-
>
91-
</UserMenu>
85+
/>
86+
<MainMessageBox
87+
v-model:show-modal="isShowModal"
88+
content="是否确认退出登录?"
89+
confirm-btn-text="确认"
90+
@confirm="signOut"
91+
/>
9292
</template>
9393

9494
<script setup lang="ts">

apps/client/components/common/Modal.vue

+57-30
Original file line numberDiff line numberDiff line change
@@ -4,78 +4,105 @@ import { ref, watchEffect } from "vue";
44
defineOptions({ name: "Modal" });
55
66
/**
7-
* @param {boolean} showModal - 是否显示弹窗
8-
* @param {boolean} modal - 是否使用 showModal
9-
* @param {boolean} closeOnClickModal - 点击遮罩层是否关闭弹窗
10-
* @param {string} modalColor - 遮罩背景色
7+
* @param {boolean} showModal - 必传,是否显示弹窗,默认不显示
8+
* @param {boolean} modal - 可选,是否显示遮罩层,默认不显示
9+
* @param {string} modalColor - 可选,设置遮罩层背景色,默认 30% 黑色透明
10+
* @param {string} offsetTop - 可选,距离顶部的偏移量,默认 -8vh,因为默认使用 modal-middle 只能往上走来调整
11+
* @param {string} twClass - 可选,给 modal-box 补充一些 Tailwind CSS
12+
* @param {boolean} closeOnClickModal - 可选,是否允许点击遮罩层关闭弹窗,默认不允许
1113
*/
1214
const props = withDefaults(
1315
defineProps<{
1416
showModal: boolean;
1517
modal?: boolean;
16-
closeOnClickModal?: boolean;
1718
modalColor?: string;
18-
offset?: string;
19+
offsetTop?: string;
20+
twClass?: string;
21+
closeOnClickModal?: boolean;
1922
}>(),
2023
{
2124
showModal: false,
2225
modal: false,
23-
closeOnClickModal: false,
2426
modalColor: "rgba(0, 0, 0, 0.3)",
25-
offset: "-8vh",
27+
offsetTop: "-8vh",
28+
twClass: "",
29+
closeOnClickModal: false,
2630
},
2731
);
2832
2933
const emits = defineEmits(["close"]);
3034
3135
const modalRef = ref<HTMLDialogElement | null>(null);
3236
33-
const onClick = (e: MouseEvent) => {
34-
if (!modalRef.value) return;
37+
// 检查 modalRef 是否存在
38+
function checkModalRef() {
39+
return !!modalRef.value;
40+
}
41+
42+
function show() {
43+
modalRef.value?.show();
44+
}
45+
46+
function close() {
47+
modalRef.value?.close();
48+
}
49+
50+
function showModal() {
51+
modalRef.value?.showModal();
52+
}
53+
54+
function handleClick(e: MouseEvent) {
55+
if (!checkModalRef()) return;
56+
3557
if (props.closeOnClickModal && e.target === modalRef.value) {
36-
onClose();
58+
handleClose();
3759
}
38-
};
60+
}
3961
40-
const onOpen = () => {
41-
if (!modalRef.value) return;
62+
function handleOpen() {
63+
if (!checkModalRef()) return;
4264
43-
props.modal ? modalRef.value.showModal() : modalRef.value.show();
44-
};
65+
props.modal ? showModal() : show();
66+
}
4567
46-
const onClose = () => {
47-
if (!modalRef.value) return;
68+
function handleClose() {
69+
if (!checkModalRef()) return;
4870
49-
modalRef.value?.close();
71+
close();
5072
emits("close");
51-
};
73+
}
5274
5375
watchEffect(() => {
54-
if (!modalRef.value) return;
76+
if (!checkModalRef()) return;
5577
78+
// 处理外层传入的 showModal 改变时,控制弹框的显示/隐藏
5679
if (props.showModal) {
57-
props.modal ? modalRef.value.showModal() : modalRef.value.show();
80+
props.modal ? showModal() : show();
5881
} else {
59-
modalRef.value.close();
82+
close();
6083
}
6184
});
6285
86+
// 外部可以设置 ref 后调用 open/close 方法控制弹框
6387
defineExpose({
64-
open: onOpen,
65-
close: onClose,
88+
open: handleOpen,
89+
close: handleClose,
6690
});
6791
</script>
6892

6993
<template>
7094
<dialog
7195
ref="modalRef"
7296
class="modal"
73-
:style="{
74-
marginTop: offset,
75-
}"
76-
@click="onClick"
97+
@click="handleClick"
7798
>
78-
<slot />
99+
<div
100+
class="modal-box bg-white dark:bg-gray-800"
101+
:class="twClass"
102+
:style="{ marginTop: offsetTop }"
103+
>
104+
<slot />
105+
</div>
79106
</dialog>
80107
</template>
81108

apps/client/components/common/ProgressBar.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div class="rounded-lg border dark:border-slate-400">
2+
<div class="rounded-lg border border-gray-300 dark:border-gray-600">
33
<div
44
class="h-full rounded-lg bg-gradient-to-r from-emerald-200 to-emerald-400 transition-all dark:from-emerald-300 dark:to-emerald-500"
55
:style="{ width: `${percentage}%` }"

apps/client/components/main/Game.vue

+7-8
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,22 @@
1010
<MainSummary />
1111
<MainShare />
1212
<MainAuthRequired />
13-
<MessageBox
14-
:content="messageContent"
15-
v-model:isShowModal="isMessageShow"
13+
<!-- TODO: 暂时先不提示(有些用户正在移动端的场景下使用)-->
14+
<!-- <MainMessageBox
15+
v-model:show-modal="isMessageShow"
1616
cancel-btn-text="确定"
17-
confirmBtnText=""
18-
></MessageBox>
17+
:content="messageContent"
18+
/> -->
1919
</template>
2020

2121
<script setup lang="ts">
2222
import { onMounted } from "vue";
2323
2424
import { courseTimer } from "~/composables/courses/courseTimer";
25-
import { useDeviceTip } from "~/composables/main/game";
25+
// import { useDeviceTip } from "~/composables/main/game";
2626
import { GameMode, useGameMode } from "~/composables/user/gameMode";
27-
import MessageBox from "./MessageBox/MessageBox.vue";
2827
29-
const { isMessageShow, messageContent } = useDeviceTip();
28+
// const { isMessageShow, messageContent } = useDeviceTip();
3029
const { currentGameMode } = useGameMode();
3130
3231
onMounted(() => {
+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<script setup lang="ts">
2+
import { useVModel } from "@vueuse/core";
3+
4+
const props = withDefaults(
5+
defineProps<{
6+
showModal: boolean;
7+
modal?: boolean;
8+
title?: string;
9+
content?: string;
10+
cancelBtnText?: string;
11+
confirmBtnText?: string;
12+
}>(),
13+
{
14+
showModal: false,
15+
modal: true,
16+
title: "提示",
17+
content: "你确定吗?",
18+
cancelBtnText: "取消",
19+
confirmBtnText: "",
20+
},
21+
);
22+
const emits = defineEmits(["confirm", "update:showModal"]);
23+
24+
// 可以在这个地方直接更新外层 showModal
25+
const isShowModal = useVModel(props, "showModal", emits);
26+
27+
function handleCancel() {
28+
isShowModal.value = false;
29+
}
30+
31+
function handleConfirm() {
32+
emits("confirm");
33+
handleCancel();
34+
}
35+
</script>
36+
37+
<template>
38+
<CommonModal
39+
:show-modal="isShowModal"
40+
:modal="modal"
41+
:close-on-click-modal="true"
42+
@close="handleCancel"
43+
>
44+
<h3 class="text-lg font-bold">{{ title }}</h3>
45+
<p class="py-4">{{ content }}</p>
46+
<div class="modal-action">
47+
<button
48+
class="btn mr-2"
49+
@click="handleCancel"
50+
>
51+
{{ cancelBtnText }}
52+
</button>
53+
<!-- TODO: 后续看看有没有更好的方案 -->
54+
<button
55+
v-if="confirmBtnText"
56+
class="btn"
57+
@click="handleConfirm"
58+
>
59+
{{ confirmBtnText }}
60+
</button>
61+
</div>
62+
</CommonModal>
63+
</template>

apps/client/components/main/MessageBox/MessageBox.vue

-46
This file was deleted.

apps/client/components/main/MessageBox/tests/message-box.spec.ts

-59
This file was deleted.

0 commit comments

Comments
 (0)