This commit is contained in:
Putoo
2026-04-26 18:53:56 +08:00
parent 2c6c62e88c
commit 4463f9e810
20 changed files with 461 additions and 1035 deletions

View File

@@ -114,6 +114,8 @@
</template>
<script setup lang="ts">
import type { IUserInfo } from '~/types/user'
definePageMeta({
layout: layout.empty
})
@@ -121,6 +123,8 @@ definePageMeta({
type ErrorField = 'username' | 'password' | 'agreement'
const isSubmitting = ref(false)
const userStore = useUserStore()
const { emit } = useEventBus()
const form = reactive({
username: '',
@@ -194,6 +198,23 @@ const handleSubmit = async () => {
try {
isSubmitting.value = true
await new Promise((resolve) => setTimeout(resolve, 900))
const loginAt = new Date().toISOString()
const mockUserInfo: IUserInfo = {
id: Date.now(),
username: form.username.trim(),
nickname: form.username.trim(),
role: 'user'
}
userStore.setUserInfo(mockUserInfo, `demo-token-${mockUserInfo.id}`)
emit('auth:login', {
userId: mockUserInfo.id || 0,
username: mockUserInfo.username || '',
nickname: mockUserInfo.nickname || '',
loginAt
})
showSuccessToast('登录成功')
await navigateTo('/home')
} finally {

View File

@@ -5,14 +5,13 @@
<p class="eyebrow">SignalR IM Demo</p>
<h1>即时通讯测试页</h1>
<p class="hero-text">
当前已对接服务端 `ChatHub`默认调用 `SendMessage(user, message)`监听 `ReceiveMessage`
`UserConnected` `UserDisconnected`
当前页面会连接服务端 `ChatHub`默认调用 `SendMessage(user, message)`监听 `ReceiveMessage``UserConnected``UserDisconnected`收到聊天消息时会同步发出全局事件 `chat:received`
</p>
</div>
<div class="status-box">
<span class="status-label">连接状态</span>
<strong :class="['status-pill', `is-${status}`]">{{ statusText }}</strong>
<span v-if="connectionId" class="connection-id">连接ID{{ connectionId }}</span>
<span v-if="connectionId" class="connection-id">连接 ID{{ connectionId }}</span>
</div>
</section>
@@ -34,7 +33,7 @@
<textarea
v-model.trim="accessToken"
rows="3"
placeholder="如端开启鉴权,可在这里粘贴 token为空则匿名连接。"
placeholder="如果服务端开启鉴权,可在这里粘贴 token为空则匿名连接。"
/>
</label>
@@ -48,7 +47,7 @@
<section class="panel">
<div class="panel-title">消息面板</div>
<div ref="messageListRef" class="message-list">
<div v-if="!messages.length" class="empty-state">还没有消息先连接然后发送一条试试</div>
<div v-if="!messages.length" class="empty-state">还没有消息先连接再发一条试试</div>
<article
v-for="item in messages"
:key="item.id"
@@ -113,6 +112,7 @@ interface LogItem {
}
const userStore = useUserStore()
const { emit } = useEventBus()
const defaultHubUrl = `${BaseConfig.BaseUrl.replace(/\/$/, '')}/chatHub`
@@ -193,7 +193,16 @@ const resolveHubUrl = (value: string) => {
const registerConnectionEvents = (hub: HubConnection) => {
hub.on('ReceiveMessage', (user: string, message: string) => {
addMessage(user || '匿名用户', message || '')
const sender = user || '匿名用户'
const text = message || ''
addMessage(sender, text)
emit('chat:received', {
user: sender,
message: text,
receivedAt: new Date().toISOString(),
type: 'chat'
})
})
hub.on('UserConnected', (id: string) => {
@@ -214,7 +223,7 @@ const registerConnectionEvents = (hub: HubConnection) => {
hub.onreconnected((id) => {
status.value = 'connected'
connectionId.value = id || hub.connectionId || ''
addLog(`重连成功:${connectionId.value || '无连接ID'}`)
addLog(`重连成功:${connectionId.value || '无连接 ID'}`)
})
hub.onclose((error) => {
@@ -265,7 +274,7 @@ const connectHub = async () => {
connectionId.value = hub.connectionId || ''
status.value = 'connected'
addLog(`连接成功:${connectionId.value || '未返回连接ID'}`)
addLog(`连接成功:${connectionId.value || '未返回连接 ID'}`)
addMessage('系统', 'SignalR 已连接,可以开始发送消息。', 'system')
} catch (error) {
status.value = 'error'
@@ -389,6 +398,7 @@ h1 {
max-width: 640px;
color: #665246;
font-size: 14px;
line-height: 1.7;
}
.status-box {

View File

@@ -2,14 +2,44 @@
<div class="page-home">
<section class="home-card">
<p class="page-tag">Home</p>
<h1>前端调试入口</h1>
<h1>页面事件总线</h1>
<p class="page-desc">
里先放一个即时通讯测试页入口方便直接验证 SignalR 连接收发消息和连接状态
一版只保留最小能力页面自己定义事件名自己决定 payload 结构通过 `on / emit / off / clear` 完成页面间监听和订阅
</p>
<NuxtLink class="entry-link" to="/home/im">
进入 IM Demo
</NuxtLink>
<div class="feature-list">
<div class="feature-item">
<strong>自定义事件名</strong>
<span>例如 `auth:login``chat:received``user:refresh`</span>
</div>
<div class="feature-item">
<strong>自定义数据</strong>
<span>payload 不做预设页面按业务自己约定</span>
</div>
<div class="feature-item">
<strong>自动解绑</strong>
<span>`useEventBus().on()` 在页面销毁时会自动取消监听</span>
</div>
</div>
<div class="code-card">
<pre><code>const { on, emit } = useEventBus()
on('user:refresh', (payload) =&gt; {
console.log(payload)
})
emit('user:refresh', { id: 1 })</code></pre>
</div>
<div class="entry-row">
<NuxtLink class="entry-link" to="/auth/login">
前往登录页
</NuxtLink>
<NuxtLink class="entry-link secondary" to="/home/im">
进入 IM Demo
</NuxtLink>
</div>
</section>
</div>
</template>
@@ -18,6 +48,26 @@
definePageMeta({
layout: layout.default
})
interface IUserRefreshPayload {
id: number
name: string
}
const { on, emit } = useEventBus()
// EventBus 使用示例:订阅自定义事件
on<IUserRefreshPayload>('user:refresh', (payload) => {
console.log('收到 user:refresh 事件', payload)
})
onMounted(() => {
// EventBus 使用示例:派发自定义事件
emit<IUserRefreshPayload>('user:refresh', {
id: 1,
name: 'captain'
})
})
</script>
<style scoped>
@@ -25,7 +75,11 @@ definePageMeta({
.page-home div,
.page-home section,
.page-home p,
.page-home h1 {
.page-home h1,
.page-home strong,
.page-home span,
.page-home pre,
.page-home code {
margin: 0;
}
@@ -38,7 +92,7 @@ definePageMeta({
}
.home-card {
width: min(720px, 100%);
width: min(760px, 100%);
margin: 0 auto;
padding: 24px;
border-radius: 24px;
@@ -62,8 +116,54 @@ h1 {
.page-desc {
margin-top: 12px;
color: #665246;
font-size: 15px;
line-height: 1.7;
color: #665246;
}
.feature-list {
display: grid;
gap: 12px;
margin-top: 20px;
}
.feature-item,
.code-card {
padding: 14px 16px;
border-radius: 18px;
background: rgba(255, 255, 255, 0.88);
border: 1px solid rgba(98, 77, 65, 0.12);
}
.feature-item strong {
display: block;
font-size: 15px;
color: #2a221d;
}
.feature-item span {
display: block;
margin-top: 6px;
font-size: 14px;
color: #665246;
}
.code-card {
margin-top: 16px;
overflow-x: auto;
}
.code-card code {
font-size: 14px;
line-height: 1.7;
color: #3f3129;
}
.entry-row {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-top: 20px;
}
.entry-link {
@@ -71,12 +171,19 @@ h1 {
align-items: center;
justify-content: center;
min-width: 140px;
margin-top: 20px;
padding: 12px 18px;
border-radius: 14px;
text-decoration: none;
}
.entry-link {
background: linear-gradient(135deg, #fe5e08 0%, #d9534f 100%);
color: #fff;
text-decoration: none;
}
.entry-link.secondary {
background: rgba(89, 70, 59, 0.08);
color: #4a3a31;
}
.entry-link:hover,
@@ -85,4 +192,10 @@ h1 {
color: #fff;
background: linear-gradient(135deg, #db5208 0%, #bf4744 100%);
}
@media (max-width: 768px) {
h1 {
font-size: 24px;
}
}
</style>

View File

@@ -91,16 +91,19 @@
<script setup lang="ts">
// MessageExtend.showToast('success', '更新成功!')
MessageExtend.notifyList('primary', ['获取装备'])
//MessageExtend.ShowToast('success', '更新成功!')
//MessageExtend.NotifyList("success", ['获取装备',"获取物品"])
definePageMeta({
layout: layout.empty
})
showNotify({ message: '提示' });
// await navigateTo('/auth/login', { replace: true })
onMounted(() => {
req();
alert(1);
onMounted(async () => {
const test = await LoginService.Test("dddd","dddd2");
console.log(test);
//alert(1);
})
</script>