diff --git a/index.html b/index.html
index b19040a..eccafa1 100644
--- a/index.html
+++ b/index.html
@@ -3,7 +3,8 @@
-
+
+
Vite App
diff --git a/package-lock.json b/package-lock.json
index adbada9..a0ef2aa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
"@microsoft/signalr-protocol-msgpack": "^10.0.0",
"mitt": "^3.0.1",
"pinia": "^3.0.4",
+ "pinia-plugin-persistedstate": "^4.7.1",
"update": "^0.7.4",
"vant": "^4.9.24",
"vue": "beta",
@@ -6095,6 +6096,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/defu": {
+ "version": "6.1.7",
+ "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz",
+ "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==",
+ "license": "MIT"
+ },
"node_modules/delimiter-regex": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/delimiter-regex/-/delimiter-regex-2.0.0.tgz",
@@ -11400,6 +11407,31 @@
}
}
},
+ "node_modules/pinia-plugin-persistedstate": {
+ "version": "4.7.1",
+ "resolved": "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.7.1.tgz",
+ "integrity": "sha512-WHOqh2esDlR3eAaknPbqXrkkj0D24h8shrDPqysgCFR6ghqP/fpFfJmMPJp0gETHsvrh9YNNg6dQfo2OEtDnIQ==",
+ "license": "MIT",
+ "dependencies": {
+ "defu": "^6.1.4"
+ },
+ "peerDependencies": {
+ "@nuxt/kit": ">=3.0.0",
+ "@pinia/nuxt": ">=0.10.0",
+ "pinia": ">=3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@nuxt/kit": {
+ "optional": true
+ },
+ "@pinia/nuxt": {
+ "optional": true
+ },
+ "pinia": {
+ "optional": true
+ }
+ }
+ },
"node_modules/pkg-store": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/pkg-store/-/pkg-store-0.2.2.tgz",
diff --git a/package.json b/package.json
index 48e297a..7f2400c 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"@microsoft/signalr-protocol-msgpack": "^10.0.0",
"mitt": "^3.0.1",
"pinia": "^3.0.4",
+ "pinia-plugin-persistedstate": "^4.7.1",
"update": "^0.7.4",
"vant": "^4.9.24",
"vue": "beta",
diff --git a/src/App.vue b/src/App.vue
index 96a1a4c..74bc9c9 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,11 +1,140 @@
-
+
+
+
+
+
+
+ 消息
+
+
+
+ 联系人
+
+
+
+ 我的
+
+
+
+
diff --git a/src/components/ChatTest.vue b/src/components/ChatTest.vue
index 21f5f38..6311b73 100644
--- a/src/components/ChatTest.vue
+++ b/src/components/ChatTest.vue
@@ -73,30 +73,30 @@ const messages = computed(() => chatMessages.value[currentChat.value.id] || [])
const newMessage = ref("")
onMounted(() => {
- emitter.on('SendPrivateMessage', ({ success, message, userid, messageId }) => {
- if (success) {
- if (!chatMessages.value[userid]) {
- chatMessages.value[userid] = []
+ emitter.on('SendPrivateMessage', (ResultResponse) => {
+ if (ResultResponse.success) {
+ if (!chatMessages.value[ResultResponse.senderId]) {
+ chatMessages.value[ResultResponse.senderId] = []
}
- chatMessages.value[userid].push({
- id: messageId,
- from: chats.value.find(c => c.id === userid)?.name || "未知用户",
- text: message,
+ chatMessages.value[ResultResponse.senderId].push({
+ id: ResultResponse.messageId,
+ from: chats.value.find(c => c.id === ResultResponse.senderId)?.name || "未知用户",
+ text: ResultResponse.message,
isRead: false
})
- currentChat.value.lastMessage = message
- if (currentChat.value.id === userid) {
- sendIsRead(messageId)
- console.log("收到消息,已发送已读回执:", messageId)
+ currentChat.value.lastMessage = ResultResponse.message
+ if (currentChat.value.id === ResultResponse.senderId) {
+ sendIsRead(ResultResponse.messageId)
+ console.log("收到消息,已发送已读回执:", ResultResponse.messageId)
}
} else {
chatMessages.value[currentChat.value.id].push({
- id: messageId,
+ id: ResultResponse.messageId,
from: "我",
- text: message,
+ text: ResultResponse.message,
isRead: false
})
- currentChat.value.lastMessage = message
+ currentChat.value.lastMessage = ResultResponse.message
}
})
emitter.on('SendReadReceipt', (messageId) => {
diff --git a/src/main.js b/src/main.js
index 75163b0..b693419 100644
--- a/src/main.js
+++ b/src/main.js
@@ -5,23 +5,21 @@ import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
+import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
// 引入 Vant 组件
-import { Search, Cell, List, NavBar, Card, Field, Button } from 'vant'
+import Vant from 'vant'
import 'vant/lib/index.css'
const app = createApp(App)
+const pinia = createPinia()
+pinia.use(piniaPluginPersistedstate)
// 注册 Vant 组件
-app.use(Search)
-app.use(Cell)
-app.use(List)
-app.use(NavBar)
-app.use(Card)
-app.use(Field)
-app.use(Button)
+app.use(Vant)
+
+app.use(pinia)
-app.use(createPinia())
app.use(router)
app.mount('#app')
diff --git a/src/network/signalr.js b/src/network/signalr.js
index 114fd9c..de6579b 100644
--- a/src/network/signalr.js
+++ b/src/network/signalr.js
@@ -20,20 +20,17 @@ function registerHandlers(conn) {
emitter.emit('GroupCount', { groupName, count })
})
- conn.on('RegisterResult', (success, message) => {
- emitter.emit('RegisterResult', { success, message })
- })
-
- conn.on('LoginResult', async (success, accessToken, refreshToken) => {
- emitter.emit('LoginResult', { success, accessToken, refreshToken })
- })
-
- conn.on('SendPrivateMessage', (success, message, userid, messageId) => {
- emitter.emit('SendPrivateMessage', { success, message, userid, messageId })
+ conn.on('SendPrivateMessage', (ResultResponse) => {
+ emitter.emit('SendPrivateMessage', ResultResponse)
})
conn.on('SendReadReceipt', (messageId) => {
emitter.emit('SendReadReceipt', messageId)
})
+
+ // 监听用户状态变化
+ connection.on("UserStatusChanged", (userId, isOnline) => {
+ emitter.emit('UserStatusChanged', { userId, isOnline })
+ })
}
// 初始连接(不带 token)
@@ -99,4 +96,4 @@ async function reconnectWithToken() {
await RefreshAccessToken() // 尝试刷新 token 并重连
})()
-export { connection, reconnectWithToken }
+export { connection, reconnectWithToken, createConnection }
diff --git a/src/router/index.js b/src/router/index.js
index 36e7410..9855131 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -4,23 +4,54 @@ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
- path: '/',
+ path: '/', // 消息页面
name: 'home',
- component: () => import('@/views/LoginTest.vue'),
+ component: () => import('@/views/IMessage.vue'),
},
{
- path: '/chat',
+ path: '/chat', //旧的测试聊天页面
name: 'chat',
component: () => import('@/components/HelloWorld.vue'),
},
{
- path: '/chattest',
+ path: '/chattest', //新的测试聊天页面
name: 'chattest',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('@/components/ChatTest.vue'),
},
+ {
+ path: '/contacts', //联系人页面
+ name: 'contacts',
+ component: () => import('@/views/IContacts.vue'),
+ },
+ {
+ path: '/frienddetail', //好友详情页面
+ name: 'friendDetail',
+ component: () => import('@/views/Users/FriendDetail.vue'),
+ },
+ {
+ path: '/searchcontacts', //搜索联系人页面
+ name: 'searchContacts',
+ component: () => import('@/views/Users/SearchContacts.vue'),
+ },
+ {
+ path: '/profile', //个人中心页面
+ name: 'profile',
+ component: () => import('@/views/IProfile.vue'),
+ },
+ {
+ path: '/login', //登录页面
+ name: 'login',
+ component: () => import('@/views/ILogin.vue'),
+ },
+ {
+ path: '/friendmessage', //好友消息页面
+ name: 'friendMessage',
+ component: () => import('@/views/Message/FriendMessage.vue'),
+ meta: { hideTabbar: true }
+ }
],
})
diff --git a/src/stores/friend.js b/src/stores/friend.js
new file mode 100644
index 0000000..fadbdd1
--- /dev/null
+++ b/src/stores/friend.js
@@ -0,0 +1,27 @@
+// stores/friend.js
+import { defineStore } from 'pinia'
+
+export const useFriendStore = defineStore('friend', {
+ state: () => ({
+ currentFriend: {},
+ friends: []
+ }),
+ actions: {
+ setFriend(friend) {
+ this.currentFriend = friend
+ },
+ clearFriend() {
+ this.currentFriend = null
+ }
+ },
+ persist: {
+ enabled: true,
+ strategies: [
+ {
+ key: 'friend',
+ storage: localStorage
+ }
+ ]
+ }
+})
+
diff --git a/src/stores/messages.js b/src/stores/messages.js
new file mode 100644
index 0000000..7fadaf5
--- /dev/null
+++ b/src/stores/messages.js
@@ -0,0 +1,57 @@
+// stores/messages.js
+import { defineStore } from 'pinia'
+import { useFriendStore } from './friend'
+
+export const useMessagesStore = defineStore('messages', {
+ state: () => ({
+ chats: {}, // { friendId: [消息数组] }
+ }),
+ actions: {
+ addMessage(friendId, msg) {
+ if (!this.chats[friendId]) this.chats[friendId] = []
+ const exists = this.chats[friendId].some((m) => m.id === msg.id)
+ if (!exists) {
+ this.chats[friendId].push(msg)
+ }
+ },
+
+ setMessages(friendId, msgs) {
+ const id = Number(friendId)
+ const existing = this.chats[id] || []
+ const merged = [...existing]
+
+ msgs.forEach((msg) => {
+ if (!merged.some((m) => m.id === msg.id)) {
+ merged.push(msg)
+ }
+ })
+
+ // 按消息 id 或时间排序,保证顺序正确
+ this.chats[id] = merged.sort((a, b) => a.id - b.id)
+ },
+
+ markRead(friendId, messageId) {
+ const msg = this.chats[friendId]?.find((m) => m.id === messageId)
+ if (msg) msg.isRead = true
+ },
+ },
+ getters: {
+ chatList: (state) => {
+ const friendStore = useFriendStore()
+ return Object.keys(state.chats).map((friendId) => {
+ const msgs = state.chats[friendId]
+ const lastMsg = msgs[msgs.length - 1]
+ const unreadCount = msgs.filter((m) => !m.isRead && m.from === 'other').length
+
+ const friend = friendStore.friends.find(f => f.userid === Number(friendId)) || {}
+
+ // 直接返回引用 + 补充字段
+ return {
+ ...friend, // ✅ 保持响应式引用
+ lastMessage: lastMsg ? lastMsg.text : '',
+ unreadCount
+ }
+ })
+ },
+ },
+})
diff --git a/src/stores/user.js b/src/stores/user.js
new file mode 100644
index 0000000..89fb836
--- /dev/null
+++ b/src/stores/user.js
@@ -0,0 +1,46 @@
+// stores/user.js
+import { defineStore } from 'pinia'
+
+export const useUserStore = defineStore('user', {
+ state: () => ({
+ AccessToken: '',
+ RefreshToken: '',
+ Nickname: '',
+ Id: 0,
+ AvatarUrl: '',
+ Signature: '',
+ IsOnline: false,
+ Success: false
+ }),
+ actions: {
+ setUser(data) {
+ this.AccessToken = data.AccessToken
+ this.RefreshToken = data.RefreshToken
+ this.Nickname = data.Nickname
+ this.Id = data.Id
+ this.AvatarUrl = data.AvatarUrl
+ this.Signature = data.Signature
+ this.IsOnline = data.IsOnline
+ this.Success = data.Success
+ },
+ clearUser() {
+ this.AccessToken = ''
+ this.RefreshToken = ''
+ this.Nickname = ''
+ this.Id = 0
+ this.AvatarUrl = ''
+ this.Signature = ''
+ this.IsOnline = false
+ this.Success = false
+ }
+ },
+ persist: {
+ enabled: true, // 开启持久化
+ strategies: [
+ {
+ key: 'user', // localStorage 的 key
+ storage: localStorage // 默认就是 localStorage
+ }
+ ]
+ }
+})
diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue
deleted file mode 100644
index eff59f1..0000000
--- a/src/views/HomeView.vue
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
{{ msg }}
-
- You’ve successfully created a project with
- Vite +
- Vue 3.
-
-
-
-
-
diff --git a/src/views/IContacts.vue b/src/views/IContacts.vue
new file mode 100644
index 0000000..220caa2
--- /dev/null
+++ b/src/views/IContacts.vue
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 在线
+ 离线
+
+
+
+
+
+
+
+
diff --git a/src/views/LoginTest.vue b/src/views/ILogin.vue
similarity index 58%
rename from src/views/LoginTest.vue
rename to src/views/ILogin.vue
index 35bb96a..ad7c7a9 100644
--- a/src/views/LoginTest.vue
+++ b/src/views/ILogin.vue
@@ -5,8 +5,8 @@