Skip to content

Commit c9b7ea4

Browse files
committed
增加全局通知弹框
1 parent 5e5edb1 commit c9b7ea4

File tree

4 files changed

+217
-20
lines changed

4 files changed

+217
-20
lines changed

src/App.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
<template>
22
<HelloWorld msg="Welcome to Your Vue.js App"/>
3+
<!-- 添加全局Toast组件 -->
4+
<Toast />
35
</template>
46

57
<script>
68
import HelloWorld from './components/HelloWorld.vue'
9+
import Toast from './components/common/Toast.vue'
710
811
export default {
912
name: 'App',
1013
components: {
11-
HelloWorld
14+
HelloWorld,
15+
Toast
1216
}
1317
}
1418
</script>

src/components/JsonTool.vue

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -130,17 +130,13 @@
130130
</div>
131131
</div>
132132

133-
<!-- 复制成功提示 -->
134-
<div class="toast-container" :class="{ 'toast-show': showToast }">
135-
<div class="toast-message">
136-
<i class="fas fa-check-circle toast-icon"></i>
137-
<span>{{ toastMessage }}</span>
138-
</div>
139-
</div>
133+
<!-- 移除局部 Toast 组件 -->
140134
</div>
141135
</template>
142136

143137
<script>
138+
import ToastService from '@/utils/ToastService';
139+
144140
export default {
145141
name: 'JsonTool',
146142
data() {
@@ -162,8 +158,6 @@ export default {
162158
initialWidth: 0,
163159
containerWidth: 0,
164160
collapsedLines: new Set(), // 记录被折叠的行
165-
showToast: false,
166-
toastMessage: '',
167161
collapsibleRanges: [], // 存储可折叠范围 [开始行, 结束行]
168162
lineTypes: [], // 存储每行的类型 (object-start, array-start, object-end, array-end, key-value)
169163
hoveredLine: null, // 当前鼠标悬浮的行
@@ -463,11 +457,11 @@ export default {
463457
464458
navigator.clipboard.writeText(textToCopy)
465459
.then(() => {
466-
this.showToastMessage('复制成功');
460+
ToastService.success('复制成功'); // 使用成功类型
467461
})
468462
.catch(err => {
469463
console.error('复制失败:', err);
470-
this.showToastMessage('复制失败,请手动复制');
464+
ToastService.error('复制失败,请手动复制'); // 使用错误类型
471465
});
472466
},
473467
handleHover() {
@@ -627,13 +621,7 @@ export default {
627621
628622
// 显示自定义提示消息
629623
showToastMessage(message) {
630-
this.toastMessage = message;
631-
this.showToast = true;
632-
633-
// 3秒后自动隐藏
634-
setTimeout(() => {
635-
this.showToast = false;
636-
}, 3000);
624+
ToastService.success(message);
637625
},
638626
639627
// 检查行是否可折叠
@@ -758,7 +746,7 @@ export default {
758746
// 处理JSON以显示在右侧
759747
this.processJson();
760748
// 提示用户
761-
this.showToastMessage('已插入示例JSON数据');
749+
ToastService.info('已插入示例JSON数据'); // 使用信息类型
762750
},
763751
// 压缩JSON
764752
compressJson() {

src/components/common/Toast.vue

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<template>
2+
<div class="toast-container" :class="positionClass">
3+
<transition-group name="toast">
4+
<div
5+
v-for="toast in visibleToasts"
6+
:key="toast.id"
7+
class="toast-message"
8+
:class="['toast-' + toast.type]"
9+
>
10+
<i :class="getIconClass(toast.type)" class="toast-icon"></i>
11+
<span>{{ toast.message }}</span>
12+
</div>
13+
</transition-group>
14+
</div>
15+
</template>
16+
17+
<script>
18+
import ToastService from '@/utils/ToastService';
19+
import { computed } from 'vue';
20+
21+
export default {
22+
name: 'Toast',
23+
setup() {
24+
// 获取当前可见的消息
25+
const visibleToasts = computed(() => ToastService.getVisibleToasts());
26+
27+
// 根据位置计算CSS类
28+
const positionClass = computed(() => {
29+
const positions = new Set(visibleToasts.value.map(toast => toast.position || 'top'));
30+
return Array.from(positions).map(pos => `position-${pos}`);
31+
});
32+
33+
// 获取图标类名
34+
const getIconClass = (type) => {
35+
switch (type) {
36+
case 'success': return 'fas fa-check-circle';
37+
case 'error': return 'fas fa-times-circle';
38+
case 'warning': return 'fas fa-exclamation-triangle';
39+
case 'info': return 'fas fa-info-circle';
40+
default: return 'fas fa-info-circle';
41+
}
42+
};
43+
44+
return {
45+
visibleToasts,
46+
positionClass,
47+
getIconClass,
48+
};
49+
},
50+
};
51+
</script>
52+
53+
<style scoped>
54+
.toast-container {
55+
position: fixed;
56+
z-index: 9999;
57+
display: flex;
58+
flex-direction: column;
59+
align-items: center;
60+
width: 100%;
61+
pointer-events: none;
62+
}
63+
64+
.position-top {
65+
top: 20px;
66+
}
67+
68+
.position-bottom {
69+
bottom: 20px;
70+
}
71+
72+
.toast-message {
73+
background: rgba(0, 0, 0, 0.7);
74+
color: white;
75+
padding: 10px 16px;
76+
border-radius: 20px;
77+
display: flex;
78+
align-items: center;
79+
gap: 8px;
80+
font-size: 14px;
81+
margin: 8px 0;
82+
max-width: 80%;
83+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
84+
backdrop-filter: blur(5px);
85+
-webkit-backdrop-filter: blur(5px);
86+
pointer-events: auto;
87+
}
88+
89+
.toast-success {
90+
background: rgba(38, 166, 91, 0.9);
91+
}
92+
93+
.toast-error {
94+
background: rgba(235, 59, 90, 0.9);
95+
}
96+
97+
.toast-warning {
98+
background: rgba(255, 159, 67, 0.9);
99+
}
100+
101+
.toast-info {
102+
background: rgba(0, 122, 255, 0.9);
103+
}
104+
105+
.toast-icon {
106+
font-size: 16px;
107+
}
108+
109+
/* 动画效果 */
110+
.toast-enter-active,
111+
.toast-leave-active {
112+
transition: all 0.3s ease;
113+
}
114+
115+
.toast-enter-from {
116+
opacity: 0;
117+
transform: translateY(-20px);
118+
}
119+
120+
.toast-leave-to {
121+
opacity: 0;
122+
transform: translateY(-20px);
123+
}
124+
</style>

src/utils/ToastService.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { reactive } from 'vue';
2+
3+
// 创建一个响应式状态存储 toast 状态
4+
const state = reactive({
5+
queue: [], // 消息队列
6+
visibleToasts: [], // 当前可见的消息
7+
nextId: 0, // 用于生成唯一ID
8+
});
9+
10+
// 默认配置
11+
const defaultOptions = {
12+
duration: 3000, // 消息显示时长
13+
type: 'info', // 消息类型:info, success, warning, error
14+
position: 'top', // 位置:top, bottom
15+
};
16+
17+
export default {
18+
// 向队列添加消息
19+
show(message, options = {}) {
20+
const id = state.nextId++;
21+
const toast = {
22+
id,
23+
message,
24+
...defaultOptions,
25+
...options,
26+
visible: true,
27+
createdAt: Date.now(),
28+
};
29+
30+
// 添加到队列
31+
state.queue.push(toast);
32+
33+
// 添加到可见列表
34+
state.visibleToasts.push(toast);
35+
36+
// 设置超时自动关闭
37+
setTimeout(() => {
38+
this.hide(id);
39+
}, toast.duration);
40+
41+
return id;
42+
},
43+
44+
// 隐藏指定ID的消息
45+
hide(id) {
46+
const index = state.visibleToasts.findIndex(toast => toast.id === id);
47+
if (index !== -1) {
48+
state.visibleToasts.splice(index, 1);
49+
}
50+
},
51+
52+
// 快捷方法:成功消息
53+
success(message, options = {}) {
54+
return this.show(message, { ...options, type: 'success' });
55+
},
56+
57+
// 快捷方法:错误消息
58+
error(message, options = {}) {
59+
return this.show(message, { ...options, type: 'error' });
60+
},
61+
62+
// 快捷方法:警告消息
63+
warning(message, options = {}) {
64+
return this.show(message, { ...options, type: 'warning' });
65+
},
66+
67+
// 快捷方法:信息消息
68+
info(message, options = {}) {
69+
return this.show(message, { ...options, type: 'info' });
70+
},
71+
72+
// 获取当前可见消息列表
73+
getVisibleToasts() {
74+
return state.visibleToasts;
75+
},
76+
77+
// 清除所有消息
78+
clear() {
79+
state.visibleToasts.length = 0;
80+
}
81+
};

0 commit comments

Comments
 (0)