From 6d990c97bc61380a3394d4d4fc0102f4f225b876 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Wed, 10 Jun 2026 19:45:33 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=8B=E5=8D=95=E9=94=99?= =?UTF-8?q?=E8=AF=AF=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DESKTOP_NOTIFICATION.md | 112 --------------------- frontend/src/features/orders/api/orders.ts | 31 +++--- 2 files changed, 19 insertions(+), 124 deletions(-) delete mode 100644 DESKTOP_NOTIFICATION.md diff --git a/DESKTOP_NOTIFICATION.md b/DESKTOP_NOTIFICATION.md deleted file mode 100644 index ab6c1f1..0000000 --- a/DESKTOP_NOTIFICATION.md +++ /dev/null @@ -1,112 +0,0 @@ -# 桌面通知功能说明 - -## 功能概述 - -系统已集成浏览器桌面通知功能,当收到新消息时会在 Windows/Mac 系统级别发送通知。 - -## 触发条件 - -桌面通知会在以下情况触发: -1. 收到新消息(非自己发送的) -2. 页面不在前台(浏览器最小化或切换到其他标签页) -3. 或者正在查看其他会话(不在当前消息的会话页面) - -## 通知内容 - -- **标题**:发送者角色 + 昵称(如:"客服 · 张三") -- **内容**:消息文本(最多显示100字) -- **图片消息**:显示 "[图片消息]" -- **点击通知**:自动跳转到对应会话 - -## 使用方式 - -### 用户端 -1. 进入 `/messages` 消息列表页面 -2. 点击页面右上角的铃铛图标 -3. 浏览器会弹出权限请求,点击"允许" -4. 之后收到新消息时会自动发送桌面通知 - -### 管理端 -1. 进入 `/admin/chats` 客服管理页面 -2. 首次进入会在用户交互后自动请求权限 -3. 允许权限后即可接收通知 - -### 移动端 -- iOS Safari:不支持桌面通知 -- Android Chrome:支持桌面通知 - -## 权限状态 - -- **未开启**:默认状态,首次需要点击允许 -- **已开启**:绿色铃铛图标,正常接收通知 -- **已拒绝**:用户点击了拒绝,需要在浏览器设置中手动开启 - - Chrome: 设置 → 隐私和安全 → 网站设置 → 通知 - - Safari: 偏好设置 → 网站 → 通知 - -## 技术实现 - -### 前端文件 -- `frontend/src/features/chats/composables/useDesktopNotification.ts` - 通知核心逻辑 -- `frontend/src/features/chats/components/NotificationSettings.vue` - 通知设置组件 -- `frontend/src/features/chats/views/ChatView.vue` - 用户端聊天页集成 -- `frontend/src/features/chats/views/MobileChatView.vue` - 移动端聊天页集成 -- `frontend/src/features/admin/views/AdminChatsView.vue` - 管理端集成 - -### 核心 API -```typescript -const { notify, requestPermission, permission } = useDesktopNotification('user') - -// 发送通知 -notify(chatEvent, currentConversationId) - -// 请求权限 -await requestPermission() - -// 权限状态:'default' | 'granted' | 'denied' -console.log(permission.value) -``` - -## 浏览器兼容性 - -| 浏览器 | 桌面通知支持 | -|--------|-------------| -| Chrome | ✅ 完全支持 | -| Firefox | ✅ 完全支持 | -| Safari (macOS) | ✅ 完全支持 | -| Edge | ✅ 完全支持 | -| Safari (iOS) | ❌ 不支持 | -| 微信内置浏览器 | ❌ 不支持 | - -## 通知特性 - -1. **智能去重**:相同会话的通知会自动替换(不会堆积) -2. **自动关闭**:通知5秒后自动消失 -3. **点击跳转**:点击通知会自动跳转到对应会话并聚焦浏览器窗口 -4. **不干扰当前会话**:如果正在查看该会话,不会发送通知 -5. **仅限他人消息**:自己发送的消息不会触发通知 - -## 注意事项 - -1. **首次使用需要用户授权**:浏览器安全策略要求用户主动允许 -2. **需要 HTTPS**:生产环境必须使用 HTTPS(本地开发 localhost 除外) -3. **系统勿扰模式**:如果操作系统开启了勿扰模式,通知可能不会显示 -4. **浏览器设置**:用户可以在浏览器设置中随时关闭网站通知 - -## 测试方法 - -1. 打开两个浏览器窗口(或使用隐私模式) -2. 窗口A:登录用户/客服A,进入某个会话 -3. 窗口B:登录用户/客服B,进入相同会话 -4. 在窗口B发送消息 -5. 将窗口A最小化或切换到其他标签页 -6. 应该会收到系统通知 - -## 排查问题 - -如果没有收到通知,检查: -1. 浏览器是否支持 Notification API -2. 网站通知权限是否被拒绝(检查浏览器地址栏左侧的锁图标) -3. 操作系统通知设置是否开启 -4. 浏览器是否开启了勿扰模式 -5. 是否在查看当前会话(查看当前会话不会发通知) -6. 是否是自己发送的消息(自己发送的不会通知) diff --git a/frontend/src/features/orders/api/orders.ts b/frontend/src/features/orders/api/orders.ts index d36ab3d..77c8298 100644 --- a/frontend/src/features/orders/api/orders.ts +++ b/frontend/src/features/orders/api/orders.ts @@ -185,7 +185,7 @@ export async function cancelOrder(id: number) { } export async function submitHandoff(id: number, content: string) { - const { data } = await apiClient.post>(`/orders/${id}/handoffs`, { + const { data } = await apiClient.post>(`/orders/${id}/handoff`, { content, }) return data.data @@ -200,22 +200,27 @@ export async function confirmHandoff(orderId: number, type: string, content: str } export async function fetchHandoffRecords(id: string | number) { - const { data } = await apiClient.get>(`/orders/${id}/handoffs`) - return data.data + const { data } = await apiClient.get>( + `/orders/${id}/handoff-records` + ) + return Array.isArray(data.data?.items) ? data.data.items : [] } export async function confirmReceive(id: number) { const { data } = await apiClient.post>( - `/orders/${id}/receive` + `/orders/${id}/confirm-receive` ) return data.data } export async function startOrderPayment(orderId: number, req: StartOrderPaymentRequest = {}) { - const { data } = await apiClient.post>(`/orders/${orderId}/pay`, { - pay_way: req.pay_way || 'wechat', - jspay_flag: req.jspay_flag || '', - }) + const { data } = await apiClient.post>( + `/orders/${orderId}/start-payment`, + { + pay_way: req.pay_way || 'ZFBZF', + jspay_flag: req.jspay_flag || '', + } + ) return data.data } @@ -224,7 +229,7 @@ export function startPayment(orderId: number, payWay: string, jsPayFlag?: string } export async function queryOrderPayment(orderId: number) { - const { data } = await apiClient.post>(`/orders/${orderId}/pay/query`) + const { data } = await apiClient.get>(`/orders/${orderId}/query-payment`) return data.data } @@ -286,8 +291,10 @@ export async function fetchAdminOrder(id: string | number) { } export async function fetchAdminHandoffRecords(id: string | number) { - const { data } = await apiClient.get>(`/admin/orders/${id}/handoffs`) - return data.data + const { data } = await apiClient.get>( + `/admin/orders/${id}/handoff-records` + ) + return Array.isArray(data.data?.items) ? data.data.items : [] } export async function adminCloseOrder(id: number, reason: string) { @@ -296,7 +303,7 @@ export async function adminCloseOrder(id: number, reason: string) { } export async function adminMarkOrderAbnormal(id: number, reason: string) { - const { data } = await apiClient.post>(`/admin/orders/${id}/abnormal`, { + const { data } = await apiClient.post>(`/admin/orders/${id}/mark-abnormal`, { reason, }) return data.data