- 按分类计数改分类总数(sysTotal/bizTotal/approvalTotal,与列表内容对齐),已读=已读总数、全部=全部总数 - 按应用角标改 countByApp 按应用消息总数(含已读,已读后不清零),总数>0 才显示、总数降序 - 应用匹配/筛选全面改 appId:行与计数出参带 appId、筛选传 appId,appKey 不再出现在消息中心前端
This commit is contained in:
+13
-9
@@ -107,18 +107,18 @@ class Base extends Abstract {
|
|||||||
// ===== 消息中心代理(§3.5:经后端签名代理 UserMsg,前端不直连)=====
|
// ===== 消息中心代理(§3.5:经后端签名代理 UserMsg,前端不直连)=====
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息中心分页列表({total, list[]};元素含改写后的绝对 jumpUrl)。
|
* 消息中心分页列表({total, list[]};元素含改写后的绝对 jumpUrl、appId/appName)。
|
||||||
* 可选服务端筛选:msgType 0/1/2、appKey、isRead 0/1、keyword 标题/内容模糊。
|
* 可选服务端筛选:msgType 0/1/2、appId(注册表应用 id)、isRead 0/1、keyword 标题/内容模糊。
|
||||||
* @memberof Basic
|
* @memberof Basic
|
||||||
*/
|
*/
|
||||||
getMsgCenterList(params: PageParams & { msgType?: number; appKey?: string; isRead?: number; keyword?: string }) {
|
getMsgCenterList(params: PageParams & { msgType?: number; appId?: string; isRead?: number; keyword?: string }) {
|
||||||
const url = path + "/msgCenter/list";
|
const url = path + "/msgCenter/list";
|
||||||
return this.getRequest<PageData>({ url, params });
|
return this.getRequest<PageData>({ url, params });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 铃铛摘要:未读数 + 各应用待办角标 + 分类计数(sysUnread/bizUnread/approvalUnread/total/readCount,
|
* 铃铛摘要:未读数 + 各应用待办角标 + 分类计数(sysUnread/bizUnread/approvalUnread 为未读口径,
|
||||||
* 旧版 UserMsg 无 summary 端点时这些字段缺省,前端按降级处理)
|
* sysTotal/bizTotal/approvalTotal/total/readCount 为总数口径;旧版 UserMsg 无这些端点字段时缺省,前端按降级处理)
|
||||||
* @memberof Basic
|
* @memberof Basic
|
||||||
*/
|
*/
|
||||||
getMsgCenterSummary() {
|
getMsgCenterSummary() {
|
||||||
@@ -129,18 +129,22 @@ class Base extends Abstract {
|
|||||||
sysUnread?: number;
|
sysUnread?: number;
|
||||||
bizUnread?: number;
|
bizUnread?: number;
|
||||||
approvalUnread?: number;
|
approvalUnread?: number;
|
||||||
|
sysTotal?: number;
|
||||||
|
bizTotal?: number;
|
||||||
|
approvalTotal?: number;
|
||||||
total?: number;
|
total?: number;
|
||||||
readCount?: number;
|
readCount?: number;
|
||||||
}>({ url });
|
}>({ url });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息中心:按来源应用未读数映射(「按应用」清单角标):{appKey: 未读数},无未读的 appKey 不出现
|
* 消息中心:按来源应用消息计数(「按应用」清单角标):[{appId, appName, total, unread}],
|
||||||
|
* total 含已读;无消息的应用不出现
|
||||||
* @memberof Basic
|
* @memberof Basic
|
||||||
*/
|
*/
|
||||||
getMsgCenterUnreadCountByApp() {
|
getMsgCenterCountByApp() {
|
||||||
const url = path + "/msgCenter/unreadCountByApp";
|
const url = path + "/msgCenter/countByApp";
|
||||||
return this.getRequest<Record<string, number>>({ url });
|
return this.getRequest<{ appId: number | null; appName: string | null; total: number; unread: number }[]>({ url });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+36
-35
@@ -23,7 +23,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 模式二:按应用(应用清单=GetUserApp 用户应用全集;角标=unreadCountByApp 按应用未读数) -->
|
<!-- 模式二:按应用(应用清单=GetUserApp 用户应用全集;角标=countByApp 消息总数,0 不显示) -->
|
||||||
<div v-show="mode === 'app'">
|
<div v-show="mode === 'app'">
|
||||||
<div class="side-title">全部应用</div>
|
<div class="side-title">全部应用</div>
|
||||||
<div :class="['cat', { active: curApp === 'all' }]" @click="onClickApp('all')">
|
<div :class="['cat', { active: curApp === 'all' }]" @click="onClickApp('all')">
|
||||||
@@ -32,9 +32,9 @@
|
|||||||
<div class="side-sep"></div>
|
<div class="side-sep"></div>
|
||||||
<div
|
<div
|
||||||
v-for="a in appList"
|
v-for="a in appList"
|
||||||
:key="a.appKey"
|
:key="a.appId"
|
||||||
:class="['cat', { active: curApp === a.appKey }]"
|
:class="['cat', { active: curApp === String(a.appId) }]"
|
||||||
@click="onClickApp(a.appKey)"
|
@click="onClickApp(String(a.appId))"
|
||||||
>
|
>
|
||||||
<span class="dot dot-primary"></span>{{ a.name }}<span class="cnt">{{ a.cnt }}</span>
|
<span class="dot dot-primary"></span>{{ a.name }}<span class="cnt">{{ a.cnt }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -129,7 +129,7 @@ interface MsgRow {
|
|||||||
content: string;
|
content: string;
|
||||||
msgType: number; // 0 系统通知 / 1 业务提醒 / 2 审批待办
|
msgType: number; // 0 系统通知 / 1 业务提醒 / 2 审批待办
|
||||||
appName?: string | null;
|
appName?: string | null;
|
||||||
appKey?: string | null;
|
appId?: number | null; // 注册表应用 id(appKey 不下发前端)
|
||||||
sendTime?: string; // yyyy-MM-dd HH:mm:ss
|
sendTime?: string; // yyyy-MM-dd HH:mm:ss
|
||||||
isRead: number; // 1=已读
|
isRead: number; // 1=已读
|
||||||
jumpUrl?: string | null;
|
jumpUrl?: string | null;
|
||||||
@@ -158,39 +158,40 @@ const loadError = ref(false);
|
|||||||
const hasMore = computed(() => rows.value.length < total.value);
|
const hasMore = computed(() => rows.value.length < total.value);
|
||||||
const refMsgBody = ref<HTMLElement | null>(null);
|
const refMsgBody = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
/** summary 左栏计数(旧版 UserMsg 无 summary 端点时分类字段缺省 → 显示 —) */
|
/** summary 左栏计数(分类总数口径;旧版 UserMsg 无字段时缺省 → 显示 —) */
|
||||||
const catCounts = ref<{ sysUnread?: number; bizUnread?: number; approvalUnread?: number; total?: number; readCount?: number }>({});
|
const catCounts = ref<{ sysTotal?: number; bizTotal?: number; approvalTotal?: number; total?: number; readCount?: number }>({});
|
||||||
const summaryTotalHint = computed(() => (catCounts.value.total == null ? "—" : String(catCounts.value.total)));
|
const summaryTotalHint = computed(() => (catCounts.value.total == null ? "—" : String(catCounts.value.total)));
|
||||||
|
|
||||||
function isUnread(m: MsgRow) {
|
function isUnread(m: MsgRow) {
|
||||||
return m.isRead !== 1;
|
return m.isRead !== 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** appName 缺失降级:按 appKey 匹配用户应用清单(与桌面网格同源),再退类型标签 */
|
/** appName 缺失降级:按 appId 匹配用户应用清单(与桌面网格同源),再退类型标签 */
|
||||||
function typeAppName(m: MsgRow) {
|
function typeAppName(m: MsgRow) {
|
||||||
if (m.appKey) {
|
if (m.appId != null) {
|
||||||
const hit = userApps.value.find(a => a.appKey === String(m.appKey));
|
const hit = userApps.value.find(a => a.appId === m.appId);
|
||||||
if (hit && hit.name) return hit.name;
|
if (hit && hit.name) return hit.name;
|
||||||
}
|
}
|
||||||
return typeName(m.msgType);
|
return typeName(m.msgType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 分类导航(计数走 summary:分类未读数;「已读」= 已读总数) */
|
/** 分类导航(计数=分类消息总数含已读,与列表内容对齐;「全部」=全部总数、「已读」=已读总数) */
|
||||||
const catList = computed(() => [
|
const catList = computed(() => [
|
||||||
{ key: "all", name: "全部消息", color: "#5979ea", cnt: catCounts.value.total == null ? "—" : catCounts.value.total },
|
{ key: "all", name: "全部消息", color: "#5979ea", cnt: catCounts.value.total == null ? "—" : catCounts.value.total },
|
||||||
{ key: "sys", name: "系统通知", color: "#5979ea", cnt: catCounts.value.sysUnread == null ? "—" : catCounts.value.sysUnread },
|
{ key: "sys", name: "系统通知", color: "#5979ea", cnt: catCounts.value.sysTotal == null ? "—" : catCounts.value.sysTotal },
|
||||||
{ key: "biz", name: "业务提醒", color: "#e9b954", cnt: catCounts.value.bizUnread == null ? "—" : catCounts.value.bizUnread },
|
{ key: "biz", name: "业务提醒", color: "#e9b954", cnt: catCounts.value.bizTotal == null ? "—" : catCounts.value.bizTotal },
|
||||||
{ key: "appr", name: "审批待办", color: "#e8684a", cnt: catCounts.value.approvalUnread == null ? "—" : catCounts.value.approvalUnread },
|
{ key: "appr", name: "审批待办", color: "#e8684a", cnt: catCounts.value.approvalTotal == null ? "—" : catCounts.value.approvalTotal },
|
||||||
{ key: "read", name: "已读", color: "#c0c6ce", cnt: catCounts.value.readCount == null ? "—" : catCounts.value.readCount },
|
{ key: "read", name: "已读", color: "#c0c6ce", cnt: catCounts.value.readCount == null ? "—" : catCounts.value.readCount },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/** 应用导航:清单=用户应用全集(GetUserApp,BoaApp 自带 appKey/name,与桌面网格同源);角标=按应用未读数 */
|
/** 应用导航:清单=用户应用全集(GetUserApp);角标=countByApp 按应用消息总数(含已读),0 不显示 */
|
||||||
const userApps = ref<{ appKey: string; name: string }[]>([]);
|
const userApps = ref<{ appId: number; name: string }[]>([]);
|
||||||
const unreadByApp = ref<Record<string, number>>({});
|
const appCountByAppId = ref<Record<number, { total: number; unread: number }>>({});
|
||||||
const appList = computed(() =>
|
const appList = computed(() =>
|
||||||
userApps.value
|
userApps.value
|
||||||
.map(a => ({ appKey: a.appKey, name: a.name, cnt: unreadByApp.value[a.appKey] || 0 }))
|
.map(a => ({ appId: a.appId, name: a.name, cnt: appCountByAppId.value[a.appId]?.total || 0 }))
|
||||||
.sort((x, y) => y.cnt - x.cnt) // 未读多的排前(稳定排序,同计数保持注册表顺序)
|
.filter(a => a.cnt > 0)
|
||||||
|
.sort((x, y) => y.cnt - x.cnt) // 总数多的排前(稳定排序,同计数保持注册表顺序)
|
||||||
);
|
);
|
||||||
|
|
||||||
/** 应用清单一次拉齐(GetUserApp;失败降级空,不打扰列表) */
|
/** 应用清单一次拉齐(GetUserApp;失败降级空,不打扰列表) */
|
||||||
@@ -199,8 +200,8 @@ async function loadApps() {
|
|||||||
const groups: any[] = await BaseApi.getUserApp(store.getCurUserId, store.getCurSchId);
|
const groups: any[] = await BaseApi.getUserApp(store.getCurUserId, store.getCurSchId);
|
||||||
userApps.value = (groups || [])
|
userApps.value = (groups || [])
|
||||||
.flatMap((g: any) => (g && Array.isArray(g.apps) ? g.apps : []))
|
.flatMap((g: any) => (g && Array.isArray(g.apps) ? g.apps : []))
|
||||||
.filter((a: any) => a && a.appKey != null)
|
.filter((a: any) => a && a.id != null)
|
||||||
.map((a: any) => ({ appKey: String(a.appKey), name: a.name }));
|
.map((a: any) => ({ appId: Number(a.id), name: a.name }));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
/* 应用清单失败降级空 */
|
/* 应用清单失败降级空 */
|
||||||
}
|
}
|
||||||
@@ -213,13 +214,13 @@ const curTitle = computed(() => {
|
|||||||
return hit ? hit.name : "全部消息";
|
return hit ? hit.name : "全部消息";
|
||||||
}
|
}
|
||||||
if (curApp.value === "all") return "全部应用消息";
|
if (curApp.value === "all") return "全部应用消息";
|
||||||
const hit = appList.value.find(a => a.appKey === curApp.value);
|
const hit = appList.value.find(a => String(a.appId) === curApp.value);
|
||||||
return hit ? hit.name : "全部应用消息";
|
return hit ? hit.name : "全部应用消息";
|
||||||
});
|
});
|
||||||
|
|
||||||
/** 当前筛选条件 → 服务端参数(筛选语义归服务端,本地不过滤) */
|
/** 当前筛选条件 → 服务端参数(筛选语义归服务端,本地不过滤;应用筛选传注册表 appId) */
|
||||||
function currentParams(extra: { page: number }) {
|
function currentParams(extra: { page: number }) {
|
||||||
const params: { page: number; count: number; msgType?: number; appKey?: string; isRead?: number; keyword?: string } = {
|
const params: { page: number; count: number; msgType?: number; appId?: string; isRead?: number; keyword?: string } = {
|
||||||
page: extra.page,
|
page: extra.page,
|
||||||
count: PAGE_SIZE,
|
count: PAGE_SIZE,
|
||||||
};
|
};
|
||||||
@@ -229,7 +230,7 @@ function currentParams(extra: { page: number }) {
|
|||||||
else if (curCat.value === "appr") params.msgType = 2;
|
else if (curCat.value === "appr") params.msgType = 2;
|
||||||
else if (curCat.value === "read") params.isRead = 1;
|
else if (curCat.value === "read") params.isRead = 1;
|
||||||
} else if (curApp.value !== "all") {
|
} else if (curApp.value !== "all") {
|
||||||
params.appKey = curApp.value;
|
params.appId = curApp.value;
|
||||||
}
|
}
|
||||||
const kw = searchText.value.trim();
|
const kw = searchText.value.trim();
|
||||||
if (kw) params.keyword = kw;
|
if (kw) params.keyword = kw;
|
||||||
@@ -270,28 +271,28 @@ function onScroll() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 左栏计数一次拉齐(未读总数进 store 与铃铛同源;按应用未读数一并拉取) */
|
/** 左栏计数一次拉齐(未读总数进 store 与铃铛同源;按应用消息计数一并拉取) */
|
||||||
async function loadSummary() {
|
async function loadSummary() {
|
||||||
try {
|
try {
|
||||||
const [s, byApp] = await Promise.all([
|
const [s, byApp] = await Promise.all([
|
||||||
BaseApi.getMsgCenterSummary(),
|
BaseApi.getMsgCenterSummary(),
|
||||||
BaseApi.getMsgCenterUnreadCountByApp().catch(() => null),
|
BaseApi.getMsgCenterCountByApp().catch(() => null),
|
||||||
]);
|
]);
|
||||||
if (s) {
|
if (s) {
|
||||||
store.setUnreadCount(s.unreadCount);
|
store.setUnreadCount(s.unreadCount);
|
||||||
catCounts.value = {
|
catCounts.value = {
|
||||||
sysUnread: (s as any).sysUnread,
|
sysTotal: (s as any).sysTotal,
|
||||||
bizUnread: (s as any).bizUnread,
|
bizTotal: (s as any).bizTotal,
|
||||||
approvalUnread: (s as any).approvalUnread,
|
approvalTotal: (s as any).approvalTotal,
|
||||||
total: (s as any).total,
|
total: (s as any).total,
|
||||||
readCount: (s as any).readCount,
|
readCount: (s as any).readCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const map: Record<string, number> = {};
|
const map: Record<number, { total: number; unread: number }> = {};
|
||||||
if (byApp && typeof byApp === "object") {
|
((byApp as any) || []).forEach((e: any) => {
|
||||||
Object.entries(byApp).forEach(([k, v]) => (map[k] = Number(v) || 0));
|
if (e && e.appId != null) map[Number(e.appId)] = { total: Number(e.total) || 0, unread: Number(e.unread) || 0 };
|
||||||
}
|
});
|
||||||
unreadByApp.value = map;
|
appCountByAppId.value = map;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
/* 摘要失败不打扰列表 */
|
/* 摘要失败不打扰列表 */
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user