feat(弹窗组件): 新增纯信息弹窗showMessage

This commit is contained in:
hzj 2026-03-26 15:11:55 +08:00
parent 7b9472258b
commit 9cf9464d9c
2 changed files with 78 additions and 45 deletions

View File

@ -1,19 +1,19 @@
<template> <template>
<div v-if="visible" class="modal-overlay" @click="handleOverlayClick"> <div v-if="visible" class="modal-overlay" @click="handleOverlayClick">
<div class="modal" :class="[animationClass, modalTypeClass]"> <div class="modal" :class="[animationClass, modalTypeClass]">
<!-- 图标 --> <!-- 图标 - simple 类型不显示 -->
<div class="modal-icon"> <div class="modal-icon" v-if="!isSimple">
<div class="icon-circle" :class="iconCircleClass"> <div class="icon-circle" :class="iconCircleClass">
<span class="icon-symbol" :class="iconSymbolClass">{{ iconSymbol }}</span> <span class="icon-symbol" :class="iconSymbolClass">{{ iconSymbol }}</span>
</div> </div>
</div> </div>
<!-- 标题 --> <!-- 标题 - simple 类型不显示 -->
<div class="modal-title" v-if="title">{{ title }}</div> <div class="modal-title" v-if="title && !isSimple">{{ title }}</div>
<!-- 消息内容 --> <!-- 消息内容 -->
<div class="modal-message">{{ message }}</div> <div class="modal-message">{{ message }}</div>
<!-- 按钮 --> <!-- 按钮 -->
<div class="modal-actions"> <div class="modal-actions">
<button class="modal-btn" :class="buttonClass" @click="confirm">{{ confirmText }}</button> <button class="modal-btn" :class="buttonClass" @click="confirm">{{ confirmText }}</button>
@ -28,29 +28,29 @@ import { ref, onMounted, computed } from 'vue'
const props = defineProps({ const props = defineProps({
title: { title: {
type: String, type: String,
default: 'Notice' default: 'Notice',
}, },
message: { message: {
type: String, type: String,
required: true required: true,
}, },
type: { type: {
type: String, type: String,
default: 'error', // 'error' | 'warning' | 'success' | 'info' default: 'error', // 'error' | 'warning' | 'success' | 'info'
validator: (value) => ['error', 'warning', 'success', 'info'].includes(value) validator: (value) => ['error', 'warning', 'success', 'info', 'simple'].includes(value), // simple
}, },
confirmText: { confirmText: {
type: String, type: String,
default: 'OK' default: 'OK',
}, },
clickToClose: { clickToClose: {
type: Boolean, type: Boolean,
default: true default: true,
}, },
duration: { duration: {
type: Number, type: Number,
default: null // default: null, //
} },
}) })
const emit = defineEmits(['confirm', 'close']) const emit = defineEmits(['confirm', 'close'])
@ -58,13 +58,16 @@ const emit = defineEmits(['confirm', 'close'])
const visible = ref(false) const visible = ref(false)
const animationClass = ref('') const animationClass = ref('')
//
const isSimple = computed(() => props.type === 'simple')
// //
const iconSymbol = computed(() => { const iconSymbol = computed(() => {
const symbols = { const symbols = {
error: '✕', error: '✕',
warning: '⚠', warning: '⚠',
success: '✓', success: '✓',
info: 'i' info: 'i',
} }
return symbols[props.type] || symbols.error return symbols[props.type] || symbols.error
}) })
@ -80,7 +83,7 @@ const buttonClass = computed(() => `modal-btn-${props.type}`)
const show = () => { const show = () => {
visible.value = true visible.value = true
animationClass.value = 'modal-enter' animationClass.value = 'modal-enter'
// //
if (props.duration && props.duration > 0) { if (props.duration && props.duration > 0) {
setTimeout(() => { setTimeout(() => {
@ -116,7 +119,7 @@ onMounted(() => {
// //
defineExpose({ defineExpose({
show, show,
close close,
}) })
</script> </script>
@ -139,7 +142,9 @@ defineExpose({
.modal { .modal {
background: white; background: white;
border-radius: 16px; border-radius: 16px;
box-shadow: 0 20px 25px rgba(0, 0, 0, 0.1), 0 10px 10px rgba(0, 0, 0, 0.04); box-shadow:
0 20px 25px rgba(0, 0, 0, 0.1),
0 10px 10px rgba(0, 0, 0, 0.04);
width: 100%; width: 100%;
max-width: 320px; max-width: 320px;
padding: 24px 20px 20px; padding: 24px 20px 20px;
@ -175,7 +180,7 @@ defineExpose({
.modal-title { .modal-title {
font-size: 18px; font-size: 18px;
font-weight: 600; font-weight: 600;
color: #1F2937; color: #1f2937;
margin-bottom: 12px; margin-bottom: 12px;
line-height: 1.4; line-height: 1.4;
} }
@ -183,7 +188,7 @@ defineExpose({
/* 消息内容 */ /* 消息内容 */
.modal-message { .modal-message {
font-size: 14px; font-size: 14px;
color: #6B7280; color: #6b7280;
line-height: 1.5; line-height: 1.5;
margin-bottom: 20px; margin-bottom: 20px;
word-wrap: break-word; word-wrap: break-word;
@ -213,52 +218,52 @@ defineExpose({
/* Error 类型样式 */ /* Error 类型样式 */
.modal-error .icon-circle-error { .modal-error .icon-circle-error {
background-color: #FEE2E2; background-color: #fee2e2;
border-color: #FECACA; border-color: #fecaca;
} }
.modal-error .icon-symbol-error { .modal-error .icon-symbol-error {
color: #DC2626; color: #dc2626;
} }
.modal-error .modal-btn-error { .modal-error .modal-btn-error {
background-color: #DC2626; background-color: #dc2626;
} }
.modal-error .modal-btn-error:hover { .modal-error .modal-btn-error:hover {
background-color: #B91C1C; background-color: #b91c1c;
} }
.modal-error .modal-btn-error:active { .modal-error .modal-btn-error:active {
background-color: #991B1B; background-color: #991b1b;
} }
/* Warning 类型样式 */ /* Warning 类型样式 */
.modal-warning .icon-circle-warning { .modal-warning .icon-circle-warning {
background-color: #FEF3C7; background-color: #fef3c7;
border-color: #FDE68A; border-color: #fde68a;
} }
.modal-warning .icon-symbol-warning { .modal-warning .icon-symbol-warning {
color: #D97706; color: #d97706;
} }
.modal-warning .modal-btn-warning { .modal-warning .modal-btn-warning {
background-color: #D97706; background-color: #d97706;
} }
.modal-warning .modal-btn-warning:hover { .modal-warning .modal-btn-warning:hover {
background-color: #B45309; background-color: #b45309;
} }
.modal-warning .modal-btn-warning:active { .modal-warning .modal-btn-warning:active {
background-color: #92400E; background-color: #92400e;
} }
/* Success 类型样式 */ /* Success 类型样式 */
.modal-success .icon-circle-success { .modal-success .icon-circle-success {
background-color: #D1FAE5; background-color: #d1fae5;
border-color: #A7F3D0; border-color: #a7f3d0;
} }
.modal-success .icon-symbol-success { .modal-success .icon-symbol-success {
@ -274,29 +279,48 @@ defineExpose({
} }
.modal-success .modal-btn-success:active { .modal-success .modal-btn-success:active {
background-color: #065F46; background-color: #065f46;
} }
/* Info 类型样式 */ /* Info 类型样式 */
.modal-info .icon-circle-info { .modal-info .icon-circle-info {
background-color: #DBEAFE; background-color: #dbeafe;
border-color: #BFDBFE; border-color: #bfdbfe;
} }
.modal-info .icon-symbol-info { .modal-info .icon-symbol-info {
color: #2563EB; color: #2563eb;
} }
.modal-info .modal-btn-info { .modal-info .modal-btn-info {
background-color: #2563EB; background-color: #2563eb;
} }
.modal-info .modal-btn-info:hover { .modal-info .modal-btn-info:hover {
background-color: #1D4ED8; background-color: #1d4ed8;
} }
.modal-info .modal-btn-info:active { .modal-info .modal-btn-info:active {
background-color: #1E40AF; background-color: #1e40af;
}
/* 新增 simple 类型样式 */
.modal-simple .modal-message {
font-size: 15px;
color: #374151;
margin-bottom: 24px;
}
.modal-simple .modal-btn {
background-color: #2563eb;
}
.modal-simple .modal-btn:hover {
background-color: #1d4ed8;
}
.modal-simple .modal-btn:active {
background-color: #1e40af;
} }
/* 动画效果 */ /* 动画效果 */
@ -350,20 +374,20 @@ defineExpose({
max-width: 280px; max-width: 280px;
padding: 20px 16px 16px; padding: 20px 16px 16px;
} }
.icon-circle { .icon-circle {
width: 48px; width: 48px;
height: 48px; height: 48px;
} }
.icon-symbol { .icon-symbol {
font-size: 20px; font-size: 20px;
} }
.modal-title { .modal-title {
font-size: 16px; font-size: 16px;
} }
.modal-message { .modal-message {
font-size: 13px; font-size: 13px;
} }

View File

@ -109,6 +109,15 @@ export const showToast = (message, options = {}) => {
return errorModal.show(message, defaultOptions) return errorModal.show(message, defaultOptions)
} }
export const showMessage = (message, options = {}) => {
const defaultOptions = {
confirmText: i18n.global.t('ok') || 'OK',
type: 'simple',
...options,
}
return errorModal.show(message, defaultOptions)
}
// 错误处理的专用方法 // 错误处理的专用方法
export const handleError = (error) => { export const handleError = (error) => {
let message = i18n.global.t('unknown_error') || 'An unknown error occurred' let message = i18n.global.t('unknown_error') || 'An unknown error occurred'