From 9ebd0b22954b93584d6d3dcc32260c094cfd2b19 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Wed, 12 Aug 2026 18:24:05 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/layouts/AppLayout.vue | 16 +++++- src/app/router/index.ts | 2 + src/core/navigation/adminNavigation.ts | 12 +++++ src/modules/iam/pages/RolesView.vue | 4 +- src/modules/notifications/PROTOTYPE_MATRIX.md | 20 ++++++++ .../components/ContractPendingNotice.vue | 11 ++++ .../components/MessageComposerDialog.vue | 41 +++++++++++++++ .../components/TemplateEditorDialog.vue | 50 +++++++++++++++++++ .../pages/MessageManagementView.vue | 46 +++++++++++++++++ .../pages/MessageTemplatesView.vue | 37 ++++++++++++++ .../notifications/pages/MyMessagesView.vue | 41 +++++++++++++++ src/modules/notifications/routes.ts | 23 +++++++++ src/modules/notifications/types.ts | 50 +++++++++++++++++++ .../InboundFeeDetailManager.test.ts | 5 ++ .../components/InboundFeeDetailManager.vue | 10 ++-- .../locations/pages/LocationsView.vue | 15 ++++-- .../pallets/pages/PalletsView.vue | 9 ++-- .../warehouses/pages/WarehousesView.vue | 7 ++- 18 files changed, 381 insertions(+), 18 deletions(-) create mode 100644 src/modules/notifications/PROTOTYPE_MATRIX.md create mode 100644 src/modules/notifications/components/ContractPendingNotice.vue create mode 100644 src/modules/notifications/components/MessageComposerDialog.vue create mode 100644 src/modules/notifications/components/TemplateEditorDialog.vue create mode 100644 src/modules/notifications/pages/MessageManagementView.vue create mode 100644 src/modules/notifications/pages/MessageTemplatesView.vue create mode 100644 src/modules/notifications/pages/MyMessagesView.vue create mode 100644 src/modules/notifications/routes.ts create mode 100644 src/modules/notifications/types.ts diff --git a/src/app/layouts/AppLayout.vue b/src/app/layouts/AppLayout.vue index dc2a123c..84f5f67a 100644 --- a/src/app/layouts/AppLayout.vue +++ b/src/app/layouts/AppLayout.vue @@ -2,7 +2,7 @@ import { computed, ref, watch } from 'vue' import { useRoute, useRouter } from 'vue-router' import { ElMessage, ElMessageBox } from 'element-plus' -import { LogOut, Menu } from '@lucide/vue' +import { Bell, FileText, LogOut, Mail, Menu } from '@lucide/vue' import { getApiErrorDetails } from '@/shared/api/http' import baiyueLogoWhite from '@/assets/brand/baiyue-logo-white.png' import baiyueMarkWhite from '@/assets/brand/baiyue-mark-white.png' @@ -129,6 +129,16 @@ watch(() => auth.user?.id || auth.user?.account || '', (userId, previousUserId) >

{{ pageTitle }}

{{ pageDescription }}

+ + + +
{{ auth.user?.name.slice(0, 1) }}
登录身份:{{ auth.user?.name }}({{ auth.user?.account }})
@@ -153,4 +163,8 @@ watch(() => auth.user?.id || auth.user?.account || '', (userId, previousUserId) diff --git a/src/app/router/index.ts b/src/app/router/index.ts index 8bfb0e66..44b4a7cd 100644 --- a/src/app/router/index.ts +++ b/src/app/router/index.ts @@ -28,6 +28,7 @@ import { feeItemRoutes } from '@/modules/fee-items/routes' import { containerLoadingRoutes } from '@/modules/container-loading/routes' import { loadingListRoutes } from '@/modules/loading-lists/routes' import { financeRoutes } from '@/modules/finance/routes' +import { notificationRoutes } from '@/modules/notifications/routes' const router = createRouter({ history: createWebHistory(), @@ -59,6 +60,7 @@ const router = createRouter({ ...containerLoadingRoutes, ...loadingListRoutes, ...financeRoutes, + ...notificationRoutes, ...customerRoutes, ...warehouseRoutes, ...unifiedOrderRoutes, diff --git a/src/core/navigation/adminNavigation.ts b/src/core/navigation/adminNavigation.ts index 5959474f..79d80b7c 100644 --- a/src/core/navigation/adminNavigation.ts +++ b/src/core/navigation/adminNavigation.ts @@ -1,6 +1,7 @@ import type { Component } from 'vue' import { Building2, + Bell, ContactRound, ClipboardList, CircleDollarSign, @@ -180,6 +181,17 @@ export const adminNavigation: AdminNavigationNode[] = [ { key: 'download-center', title: '下载中心', icon: Download, path: '/downloads', permission: 'platform:download:list' }, ], }, + { + key: 'notifications', + title: '消息中心', + icon: Bell, + hidden: true, + children: [ + { key: 'notification-messages', title: '消息管理', icon: Bell, path: '/notifications/messages' }, + { key: 'notification-templates', title: '模板管理', icon: Bell, path: '/notifications/templates' }, + { key: 'my-notifications', title: '我的消息', icon: Bell, path: '/notifications/mine' }, + ], + }, { key: 'system', title: '系统管理', diff --git a/src/modules/iam/pages/RolesView.vue b/src/modules/iam/pages/RolesView.vue index 5cd395f0..66d9e68d 100644 --- a/src/modules/iam/pages/RolesView.vue +++ b/src/modules/iam/pages/RolesView.vue @@ -247,9 +247,9 @@ onMounted(loadRoles)
- + - + diff --git a/src/modules/notifications/PROTOTYPE_MATRIX.md b/src/modules/notifications/PROTOTYPE_MATRIX.md new file mode 100644 index 00000000..7a1896e0 --- /dev/null +++ b/src/modules/notifications/PROTOTYPE_MATRIX.md @@ -0,0 +1,20 @@ +# Admin 消息中心原型核对矩阵 + +事实来源:`docs/悦/货代SAAS平台后台/货代SaaS平台原型.html`。本矩阵只记录原型已经明确的承载方式;后端契约与权限码尚未冻结,本模块当前不请求接口、不保存本地业务数据、不模拟操作成功。 + +| 页面 | 操作 | 原型证据 | 原型承载方式 | 原型尺寸/布局 | 当前骨架 | 结论 | +|---|---|---|---|---|---|---| +| 消息管理 | 列表/查询 | `page-msg-manage`,6040-6049 行 | 页面内表格 | 类型、状态、标题/内容筛选;新建与导出位于表格工具栏 | 正式路由 `/notifications/messages`,查询栏 + 空表格 | 按项目列表规范还原;操作列移至第一列 | +| 消息管理 | 新建消息 | `modal-msg-create`,9229-9243 行;`showMsgCreate`,14661 行 | 居中弹框 | 最大宽度约 680px;基础信息、接收对象、定时发送;底部取消/草稿/立即发送 | 680px `AppDialog`,仅站内信,提交按钮禁用 | 保留原型承载;真实接收对象、草稿及发送等待契约 | +| 消息管理 | 立即/定时发送 | `sendMsg`,14678 行;定时控件 9241 行 | 新建消息弹框内 | 定时开关 + 日期时间;立即发送按钮 | 控件可核对,提交禁用 | 等待发送命令、幂等及定时规则契约 | +| 消息管理 | 草稿 | `saveMsgAsDraft`,14697 行 | 新建消息弹框内 | 独立“存为草稿”按钮 | 按钮保留但禁用 | 等待草稿创建/更新契约 | +| 消息管理 | 详情 | `renderMsgManage`,14588 行仅有“详情”按钮 | 未定义 | 未定义 | 列表操作边界预留,不创建容器 | 待产品确认弹框/抽屉/独立页面 | +| 消息管理 | 发送统计 | 表格“已读/总数”列,6048 行 | 未定义 | 仅有聚合列,无统计操作或详情容器 | 统计列预留 | 等待统计字段和承载方式确认 | +| 模板管理 | 列表/查询 | `page-msg-template`,6052-6060 行 | 页面内表格 | CODE、内容筛选;新增/导入/导出位于表格工具栏 | 正式路由 `/notifications/templates`,查询栏 + 空表格 | 操作列移至第一列;导入导出因契约缺失禁用 | +| 模板管理 | 新增模板 | `modal-msg-template-create`,9246-9274 行 | 居中弹框 | 最大宽度约 760px;变量快捷插入、字符计数、内容预览 | 760px `AppDialog`;只保留站内系统模板 | 保留原型承载,不扩展微信/邮件/短信真实渠道 | +| 模板管理 | 编辑模板 | `renderMsgTemplate`,14594 行仅有“编辑”按钮 | 未定义 | 未定义 | 列表操作边界预留,不复用新增弹框作推断 | 待产品确认承载方式与不可变字段规则 | +| 我的消息 | 列表/查询 | `page-msg-my`,6063-6071 行 | 页面内表格 | 状态、类型筛选;全部已读/清空已读位于表格工具栏 | 正式路由 `/notifications/mine`,查询栏 + 空表格 | 操作列移至第一列;批量操作禁用 | +| 我的消息 | 查看 | `renderMsgMy`,14656 行仅有“查看”按钮 | 未定义 | 未定义 | 列表操作边界预留,不创建容器 | 待产品确认承载方式 | +| 我的消息 | 标为已读/清空 | 6064、14656 行 | 列表按钮 | 单条、全部标为已读及清空已读 | 按钮预留并禁用 | 等待读状态、批量命令与删除语义契约 | +| 顶部入口 | 未读入口/三页跳转 | `topMessageMenu`,838-847 行 | 顶部下拉菜单 | 铃铛、“消息中心”、未读徽标;消息管理/模板管理/我的消息三项 | 顶部下拉骨架,未读显示“--”,菜单禁用 | 等待未读汇总接口及页面权限码后启用 | + diff --git a/src/modules/notifications/components/ContractPendingNotice.vue b/src/modules/notifications/components/ContractPendingNotice.vue new file mode 100644 index 00000000..5fe7cd5c --- /dev/null +++ b/src/modules/notifications/components/ContractPendingNotice.vue @@ -0,0 +1,11 @@ + + diff --git a/src/modules/notifications/components/MessageComposerDialog.vue b/src/modules/notifications/components/MessageComposerDialog.vue new file mode 100644 index 00000000..8af7a86d --- /dev/null +++ b/src/modules/notifications/components/MessageComposerDialog.vue @@ -0,0 +1,41 @@ + + + + + + diff --git a/src/modules/notifications/components/TemplateEditorDialog.vue b/src/modules/notifications/components/TemplateEditorDialog.vue new file mode 100644 index 00000000..591b4422 --- /dev/null +++ b/src/modules/notifications/components/TemplateEditorDialog.vue @@ -0,0 +1,50 @@ + + + + + diff --git a/src/modules/notifications/pages/MessageManagementView.vue b/src/modules/notifications/pages/MessageManagementView.vue new file mode 100644 index 00000000..37186cd4 --- /dev/null +++ b/src/modules/notifications/pages/MessageManagementView.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/src/modules/notifications/pages/MessageTemplatesView.vue b/src/modules/notifications/pages/MessageTemplatesView.vue new file mode 100644 index 00000000..33da458a --- /dev/null +++ b/src/modules/notifications/pages/MessageTemplatesView.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/modules/notifications/pages/MyMessagesView.vue b/src/modules/notifications/pages/MyMessagesView.vue new file mode 100644 index 00000000..87f51d61 --- /dev/null +++ b/src/modules/notifications/pages/MyMessagesView.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/src/modules/notifications/routes.ts b/src/modules/notifications/routes.ts new file mode 100644 index 00000000..696b2560 --- /dev/null +++ b/src/modules/notifications/routes.ts @@ -0,0 +1,23 @@ +import type { RouteRecordRaw } from 'vue-router' + +export const notificationRoutes = [ + { + path: 'notifications/messages', + name: 'notification-messages', + component: () => import('@/modules/notifications/pages/MessageManagementView.vue'), + meta: { title: '消息管理', description: '创建并管理平台站内消息' }, + }, + { + path: 'notifications/templates', + name: 'notification-templates', + component: () => import('@/modules/notifications/pages/MessageTemplatesView.vue'), + meta: { title: '模板管理', description: '维护平台站内消息模板' }, + }, + { + path: 'notifications/mine', + name: 'my-notifications', + component: () => import('@/modules/notifications/pages/MyMessagesView.vue'), + meta: { title: '我的消息', description: '查看当前平台员工收到的站内消息' }, + }, +] satisfies RouteRecordRaw[] + diff --git a/src/modules/notifications/types.ts b/src/modules/notifications/types.ts new file mode 100644 index 00000000..33d93f16 --- /dev/null +++ b/src/modules/notifications/types.ts @@ -0,0 +1,50 @@ +export type NotificationMessageType = 'SYSTEM' | 'BUSINESS' | 'ALERT' | 'TASK' +export type NotificationMessageStatus = 'DRAFT' | 'SCHEDULED' | 'SENT' | 'CANCELLED' +export type NotificationReadStatus = 'UNREAD' | 'READ' + +export interface NotificationMessageListItem { + id: string + title: string + type: NotificationMessageType + recipientSummary: string + sentAt: string | null + readCount: number | null + recipientCount: number | null + status: NotificationMessageStatus +} + +export interface NotificationInboxItem { + id: string + title: string + summary: string + type: NotificationMessageType + sentAt: string + readStatus: NotificationReadStatus +} + +export interface NotificationTemplateListItem { + id: string + code: string + title: string + content: string + type: 'SYSTEM' +} + +export interface NotificationListResult { + records: T[] + total: number + pages: number +} + +export interface NotificationComposerModel { + type: NotificationMessageType + priority: 'NORMAL' | 'IMPORTANT' | 'URGENT' + title: string + content: string + recipientScope: string + recipientIds: string[] + scheduledAt: string | null +} + +// These view models deliberately contain no endpoint paths or transport DTO names. +// Frozen backend DTOs will be mapped at the future api/ boundary instead of leaking into pages. diff --git a/src/modules/unified-orders/components/InboundFeeDetailManager.test.ts b/src/modules/unified-orders/components/InboundFeeDetailManager.test.ts index 40b834ce..2a0fffc0 100644 --- a/src/modules/unified-orders/components/InboundFeeDetailManager.test.ts +++ b/src/modules/unified-orders/components/InboundFeeDetailManager.test.ts @@ -64,6 +64,11 @@ describe('InboundFeeDetailManager', () => { expect(first).toContain('编辑'); expect(first).toContain('审核'); expect(first).toContain('驳回') expect(second).toContain('调整'); expect(second).toContain('作废'); expect(secondButtons).not.toContain('审核') expect(wrapper.find('th').text()).toContain('操作') + expect(source).not.toMatch(/]+link[^>]+(?:编辑|审核|驳回|调整|作废)/) + expect(source).toMatch(/size="small" plain type="primary"[^>]+[^>]*>\s*]+[^>]*>\s*]+[^>]*>\s*]+[^>]*>\s* { diff --git a/src/modules/unified-orders/components/InboundFeeDetailManager.vue b/src/modules/unified-orders/components/InboundFeeDetailManager.vue index de9ffdaf..281bacce 100644 --- a/src/modules/unified-orders/components/InboundFeeDetailManager.vue +++ b/src/modules/unified-orders/components/InboundFeeDetailManager.vue @@ -194,11 +194,11 @@ onMounted(() => void load())