feat(notice): 我的消息与订阅设置个人域页面

This commit is contained in:
augushong
2026-10-12 12:09:01 +00:00
parent bf82168927
commit c759812308
9 changed files with 521 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
var init = {
tableElem: '#currentTable',
tableRenderId: 'currentTableRenderId',
indexUrl: 'notice/mine',
addUrl: '',
editUrl: '',
readUrl: 'notice/read',
deleteUrl: '',
exportUrl: '',
modifyUrl: '',
};

View File

@@ -0,0 +1,7 @@
<div class="layuimini-container">
<div class="layuimini-main">
<table id="currentTable" class="layui-table layui-hide"
lay-filter="currentTable">
</table>
</div>
</div>

View File

@@ -0,0 +1,99 @@
$(function(){
// HTML 转义(标题/场景等来自发送方输入,防注入)
var escapeHtml = function(text) {
return String(text).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');
};
// 标题:关联 notice,通知主体被删时兜底;未读加粗强调
var titleTemplet = function(d) {
var notice = d.notice || {};
if (notice.title) {
var title = escapeHtml(notice.title);
if (d.read_time == 0) {
return '<strong>' + title + '</strong>';
}
return title;
}
return '<span style="color:#999">(通知已删除)#' + d.notice_id + '</span>';
};
// 场景中文映射
var sceneTemplet = function(d) {
var notice = d.notice || {};
var map = ua.getDataBrage('select_list_notice_scene') || {};
var text = map[notice.scene] || notice.scene || '-';
return escapeHtml(text);
};
// 未读/已读徽章(未读行同时以加粗标题高亮)
var readTemplet = function(d) {
if (d.read_time == 0) {
return '<span class="layui-badge">未读</span>';
}
return '<span class="layui-badge layui-bg-gray">已读</span>';
};
ua.table.render({
init: init,
toolbar: [
'refresh',
[{
text: '标记已读',
title: '确定将勾选的消息标记为已读?',
url: 'notice/mark_read',
method: 'request',
auth: 'notice/markRead',
class: 'layui-btn layui-btn-normal layui-btn-sm',
icon: 'fa fa-check',
checkbox: true,
}],
[{
text: '全部已读',
title: '确定将全部站内信标记为已读?',
url: 'notice/mark_all_read',
method: 'request',
auth: 'notice/markAllRead',
class: 'layui-btn layui-btn-warm layui-btn-sm',
icon: 'fa fa-check-square-o',
}],
],
cols: [[
{type: 'checkbox'},
{field: 'id', title: 'ID', width: 80, search: false},
{field: 'title', title: '标题', minWidth: 220, fieldAlias: 'notice.title', templet: titleTemplet},
{field: 'scene', title: '场景', width: 110, align: 'center', search: 'select', searchOp: '=', selectList: ua.getDataBrage('select_list_notice_scene'), templet: sceneTemplet},
{field: 'read_time', title: '状态', width: 80, align: 'center', search: false, templet: readTemplet},
{field: 'create_time', title: '接收时间', width: 170, templet: ua.table.date, search: 'range'},
{
width: 90, title: '操作', fixed: 'right', templet: ua.table.tool,
operat: [[
{
class: 'layui-btn layui-btn-primary layui-btn-xs notice-mine-read-btn',
method: 'none',
field: 'id',
text: '查看',
title: '查看消息',
auth: 'notice/read',
url: init.readUrl,
}
]]
}
]],
});
ua.listen();
// 查看:弹窗打开详情,关闭后刷新列表以更新未读状态
$(document).on('click', '.notice-mine-read-btn', function () {
var id = $(this).attr('data-id');
layui.layer.open({
type: 2,
title: '查看消息',
area: ['760px', '560px'],
content: ua.url(init.readUrl + '?id=' + id),
end: function () {
ua.table.reloadData(init.tableRenderId);
}
});
});
})

View File

@@ -0,0 +1,39 @@
<div class="layuimini-container">
<div class="layuimini-main">
<div class="layui-card">
<div class="layui-card-header">
<span style="font-size:16px;font-weight:bold;">{$msg.title}</span>
{if condition="$msg.read_time eq 0"}
<span class="layui-badge" style="margin-left:8px;">未读</span>
{else /}
<span class="layui-badge layui-bg-gray" style="margin-left:8px;">已读</span>
{/if}
</div>
<div class="layui-card-body">
<table class="layui-table">
<colgroup>
<col width="120">
<col>
</colgroup>
<tbody>
<tr>
<td>场景</td>
<td>{$msg.scene_title}</td>
</tr>
<tr>
<td>接收时间</td>
<td>{$msg.create_time|date="Y-m-d H:i:s"}</td>
</tr>
<tr>
<td>内容</td>
<td style="white-space:pre-wrap;">{$msg.content}</td>
</tr>
</tbody>
</table>
<div class="text-center">
<button type="button" class="layui-btn layui-btn-primary layui-btn-sm" id="notice-read-close">关闭</button>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,16 @@
$(function(){
ua.listen();
// 关闭按钮:弹窗内关闭当前 iframe;非弹窗场景(直接以标签页打开)返回上一页
$('#notice-read-close').on('click', function () {
var parentLayer = (window.parent && window.parent.layui) ? window.parent.layui.layer : null;
if (parentLayer) {
var index = parentLayer.getFrameIndex(window.name);
if (index !== undefined && index !== null) {
parentLayer.close(index);
return;
}
}
history.back();
});
})

View File

@@ -0,0 +1,52 @@
<div class="layuimini-container">
<form id="app-form" class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label">订阅设置</label>
<div class="layui-input-block">
<div style="color:#999;font-size:12px;line-height:20px;padding:4px 0 8px 0;">
选择每类通知通过哪个渠道发送给您;未保存过的组合默认为订阅。标注「必选」的场景为系统强制通知,不可关闭。
</div>
<table class="layui-table" lay-size="sm" id="subscribe-matrix">
<colgroup>
<col>
{volist name="channels" id="channelTitle"}
<col width="180">
{/volist}
</colgroup>
<thead>
<tr>
<th>场景</th>
{volist name="channels" id="channelTitle"}
<th class="text-center">{$channelTitle}</th>
{/volist}
</tr>
</thead>
<tbody>
{volist name="matrix" id="sceneRow"}
<tr>
<td>
{$sceneRow.title}
{if condition="$sceneRow.locked"}
<span class="layui-badge layui-bg-gray">必选</span>
{/if}
</td>
{volist name="sceneRow.cells" id="cell"}
<td class="text-center">
<input type="checkbox" name="{$cell.name}" value="1" lay-skin="switch" lay-text="开|关" lay-filter="subscribe-switch" {$cell.checked} {$cell.disabled}>
</td>
{/volist}
</tr>
{/volist}
</tbody>
</table>
</div>
</div>
<div class="hr-line"></div>
<div class="layui-form-item text-center">
<button type="submit" class="layui-btn layui-btn-normal layui-btn-sm" lay-submit="notice/save_subscribe" data-close="false" data-refresh="false">保存设置</button>
</div>
</form>
</div>

View File

@@ -0,0 +1,16 @@
$(function(){
// 提交前补齐矩阵:layui 只序列化勾选的开关,
// 这里把全部未禁用开关按当前状态补齐为 1/0,保证逐格显式 upsert
function buildMatrix(dataField) {
$('#subscribe-matrix input[type="checkbox"][name^="matrix["]').each(function () {
var $input = $(this);
if ($input.prop('disabled')) {
return;
}
dataField[$input.attr('name')] = $input.prop('checked') ? 1 : 0;
});
return dataField;
}
ua.listen(buildMatrix);
})