277 lines
7.7 KiB
JavaScript
277 lines
7.7 KiB
JavaScript
export class MessageContainer {
|
|
container;
|
|
id = 'for-message';
|
|
root = document.body;
|
|
constructor(opts) {
|
|
const { id } = opts || {};
|
|
if (id) {
|
|
this.id = id;
|
|
}
|
|
this.initContainer();
|
|
}
|
|
initContainer() {
|
|
const id = this.id;
|
|
const root = this.root;
|
|
let forModal = document.querySelector('#' + id);
|
|
if (!forModal) {
|
|
forModal = document.createElement('div');
|
|
forModal.id = id;
|
|
forModal.style = ``;
|
|
// 点击穿透
|
|
root.appendChild(forModal);
|
|
}
|
|
this.initStyle();
|
|
this.container = forModal;
|
|
}
|
|
initStyle(force) {
|
|
const id = this.id;
|
|
const styleId = id + '-style';
|
|
const _style = document.querySelector('#' + styleId);
|
|
if (force && _style) {
|
|
_style.remove();
|
|
}
|
|
if (!force && _style) {
|
|
return;
|
|
}
|
|
const style = document.createElement('style');
|
|
style.id = styleId;
|
|
|
|
style.innerHTML = `
|
|
#${id} {
|
|
position: fixed; top: 0; left: 0; z-index: 1000; width: 100vw;height: 100vh;pointer-events: none; display: flex; flex-direction: column; gap: 10px;
|
|
}
|
|
.message-wrapper {
|
|
display: flex;
|
|
transition: transform 2s ease-in-out, opacity 1.2s ease-in-out; /* 缩小并淡出 */
|
|
}
|
|
.message-wrapper:first-child {
|
|
margin-top: 20px;
|
|
}
|
|
.message {
|
|
display: flex;
|
|
gap: 10px;
|
|
padding: 6px 10px;
|
|
margin: 0 auto;
|
|
border-radius: 4px;
|
|
background-color: white;
|
|
box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08),
|
|
0 3px 6px -4px rgba(0, 0, 0, 0.12),
|
|
0 9px 28px 8px rgba(0, 0, 0, 0.05);
|
|
justify-content: center;
|
|
align-item: center;
|
|
animation: message-slide-down 0.3s ease-out forwards; /* 应用动画 */
|
|
}
|
|
/* 添加消失类时 */
|
|
.message-wrapper.message-hide {
|
|
transform: scale(0);
|
|
opacity: 0;
|
|
pointer-events: none; /* 防止交互 */
|
|
}
|
|
.message-success {
|
|
}
|
|
@keyframes message-slide-down {
|
|
0% {
|
|
transform: translateY(-100px); /* 从上方开始 */
|
|
opacity: 0; /* 从不可见状态开始 */
|
|
}
|
|
50% {
|
|
opacity: 0.5; /* 渐渐变为半透明 */
|
|
}
|
|
100% {
|
|
transform: translateY(0); /* 移动到初始位置 */
|
|
opacity: 1; /* 最终完全可见 */
|
|
}
|
|
}
|
|
.message-icon {
|
|
position: relative;
|
|
width: 24px;
|
|
height: 24px;
|
|
box-sizing: border-box;
|
|
}
|
|
.message-icon::before {
|
|
content: "";
|
|
position: absolute;
|
|
left: 7px;
|
|
top: 3px;
|
|
width: 5px;
|
|
height: 10px;
|
|
}
|
|
.icon-success {
|
|
border: 2px solid green; /* 外圆圈 */
|
|
border-radius: 50%; /* 使其为圆形 */
|
|
}
|
|
.icon-success::before {
|
|
border-right: 2px solid green; /* 打勾的右边部分 */
|
|
border-bottom: 2px solid green; /* 打勾的下边部分 */
|
|
transform: rotate(45deg);
|
|
}
|
|
.icon-info {
|
|
border: 2px solid blue; /* 外圆圈 */
|
|
border-radius: 50%; /* 使其为圆形 */
|
|
}
|
|
.icon-info::before {
|
|
content: "i";
|
|
position: absolute;
|
|
top: 0px;
|
|
color: blue;
|
|
font-weight: bold;
|
|
font-size: 16px;
|
|
left: 8px;
|
|
}
|
|
.icon-error::before, .icon-error::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: 4px;
|
|
left: 50%;
|
|
width: 2px;
|
|
height: 12px;
|
|
background-color: red;
|
|
transform-origin: center;
|
|
}
|
|
.icon-error {
|
|
border: 2px solid red; /* 外圆圈 */
|
|
border-radius: 50%; /* 使其为圆形 */
|
|
}
|
|
.icon-error::before {
|
|
transform: translateX(-50%) rotate(45deg); /* 旋转形成叉号的一部分 */
|
|
}
|
|
|
|
.icon-error::after {
|
|
transform: translateX(-50%) rotate(-45deg); /* 旋转形成叉号的另一部分 */
|
|
}
|
|
.icon-warning {
|
|
position: relative;
|
|
width: 0;
|
|
height: 0;
|
|
border-left: 12px solid transparent;
|
|
border-right: 12px solid transparent;
|
|
border-bottom: 24px solid orange; /* 三角形 */
|
|
display: inline-block;
|
|
transform: scale(0.8); /* 缩小三角形 */
|
|
}
|
|
|
|
.icon-warning::before {
|
|
content: "!";
|
|
position: absolute;
|
|
top: 5px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
color: white;
|
|
font-weight: bold;
|
|
font-size: 16px;
|
|
}
|
|
.icon-loading {
|
|
width: 24px;
|
|
height: 24px;
|
|
border: 3px solid #f3f3f3; /* 边框颜色,用于加载圈的背景 */
|
|
border-top: 3px solid #3498db; /* 顶部边框的颜色,用于显示加载进度 */
|
|
border-radius: 50%; /* 圆形 */
|
|
animation: spin 1s linear infinite; /* 旋转动画 */
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% {
|
|
transform: rotate(0deg);
|
|
}
|
|
100% {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
}
|
|
setRoot(root) {
|
|
if (root instanceof HTMLElement) {
|
|
this.root = root;
|
|
root.appendChild(this.container);
|
|
}
|
|
}
|
|
}
|
|
const controller = new MessageContainer();
|
|
|
|
export const createMessage = (content, opts) => {
|
|
let { icon, key, style, className, type } = opts || {};
|
|
const div = document.createElement('div');
|
|
div.className = 'message-wrapper' + (className ? ' ' + className : '');
|
|
if (style) div.style = style;
|
|
if (key) div.setAttribute('data-key', key);
|
|
const contentDiv = document.createElement('div');
|
|
contentDiv.className = 'message';
|
|
if (icon) {
|
|
const i = document.createElement('i');
|
|
i.className = icon;
|
|
i.classList.add('message-icon');
|
|
contentDiv.appendChild(i);
|
|
} else if (type) {
|
|
const i = document.createElement('div');
|
|
i.className = 'icon-' + type;
|
|
i.classList.add('message-icon');
|
|
contentDiv.appendChild(i);
|
|
}
|
|
if (content instanceof HTMLElement) {
|
|
contentDiv.appendChild(content);
|
|
} else {
|
|
const text = document.createElement('span');
|
|
text.innerText = content;
|
|
contentDiv.appendChild(text);
|
|
}
|
|
div.appendChild(contentDiv);
|
|
|
|
return div;
|
|
};
|
|
const methods = ['success', 'info', 'warning', 'error', 'loading'];
|
|
|
|
export class Message {
|
|
controller = controller;
|
|
constructor() {
|
|
this.controller = controller;
|
|
}
|
|
open = (message, timeout = 3000, onClose, opts) => {
|
|
const controller = this.controller;
|
|
const div = createMessage(message, opts);
|
|
const remove = () => {
|
|
div.classList.add('message-hide');
|
|
setTimeout(() => {
|
|
if (div?.isConnected) {
|
|
div.remove();
|
|
} else {
|
|
console.log('not connected');
|
|
controller.container.removeChild(div);
|
|
}
|
|
}, 1000);
|
|
onClose && onClose();
|
|
};
|
|
controller.container.appendChild(div);
|
|
controller.initStyle(true);
|
|
if (timeout === 0) {
|
|
return () => {
|
|
remove();
|
|
};
|
|
}
|
|
const time = setTimeout(() => {
|
|
remove();
|
|
}, timeout);
|
|
return () => {
|
|
clearTimeout(time);
|
|
remove();
|
|
};
|
|
};
|
|
success = (message, timeout = 1000, onClose = () => {}) => {
|
|
return this.open(message, timeout, onClose, { type: 'success' });
|
|
};
|
|
info = (message, timeout = 1500, onClose = () => {}) => {
|
|
return this.open(message, timeout, onClose, { type: 'info' });
|
|
};
|
|
warning = (message, timeout = 3000, onClose = () => {}) => {
|
|
return this.open(message, timeout, onClose, { type: 'warning' });
|
|
};
|
|
error = (message, timeout = 3000, onClose = () => {}) => {
|
|
return this.open(message, timeout, onClose, { type: 'error' });
|
|
};
|
|
loading = (message, timeout = 0, onClose = () => {}) => {
|
|
return this.open(message, timeout, onClose, { type: 'loading' });
|
|
};
|
|
}
|
|
|
|
export const message = new Message();
|