添加项目文件。

This commit is contained in:
2026-10-12 16:22:04 +08:00
parent 7c68a0ca0a
commit aad11be43d
1000 changed files with 120807 additions and 0 deletions
@@ -0,0 +1,408 @@
<title>公告管理</title>
<div class="layui-card layadmin-header">
<div class="layui-breadcrumb" lay-filter="breadcrumb">
<a lay-href="">主页</a>
<a><cite>系统</cite></a>
<a><cite>公告管理</cite></a>
</div>
</div>
<script src="/admin/lib/wangEditor/wangEditor.min.js"></script>
<div class="layui-fluid">
<div class="layui-card">
<div class="layui-card-body">
<table id="LAY-notice-manage" lay-filter="LAY-notice-manage"></table>
<script type="text/html" id="table-notice-toolbar">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="add"><i class="layui-icon layui-icon-add-1"></i> 发布公告</button>
<button class="layui-btn layui-btn-sm layui-btn-primary" lay-event="refresh"><i class="layui-icon layui-icon-refresh"></i> 刷新</button>
</div>
</script>
<script type="text/html" id="table-notice-bar">
<a class="layui-btn layui-btn-xs" lay-event="edit"><i class="layui-icon layui-icon-edit"></i> 编辑</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="delete"><i class="layui-icon layui-icon-delete"></i> 删除</a>
</script>
</div>
</div>
</div>
<script>
layui.use(['admin', 'table', 'form', 'layer'], function () {
var $ = layui.$,
admin = layui.admin,
table = layui.table,
form = layui.form,
layer = layui.layer,
setter = layui.setter;
var token = layui.data(setter.tableName)[setter.request.tokenName];
var baseUrl = setter.baseurl + 'adminapi/Notice';
var curPage = 1;
var curLimit = 20;
function ajaxJson(url, data, done) {
$.ajax({
url: url,
method: 'post',
contentType: 'application/json',
headers: { 'token': token },
data: JSON.stringify(data),
success: done
});
}
function loadList(page, limit) {
curPage = page || 1;
curLimit = limit || 20;
ajaxJson(baseUrl + '/list', { pageIndex: curPage, pageSize: curLimit }, function (res) {
table.reload('LAY-notice-manage', {
data: res.data || [],
count: res.count || 0,
page: { curr: curPage, limit: curLimit }
});
});
}
table.render({
elem: '#LAY-notice-manage',
data: [],
page: true,
limit: 20,
limits: [10, 20, 50, 100],
toolbar: '#table-notice-toolbar',
defaultToolbar: [],
cols: [[
{ type: 'numbers', title: '序号', width: 60 },
{ field: 'title', title: '公告标题', minWidth: 200 },
{ field: 'recommend', title: '简介', minWidth: 200 },
{ field: 'topImage', title: '顶部图片', width: 120, templet: function(d) { return d.topImage ? '<img src="' + d.topImage + '" style="width:60px;height:36px;object-fit:cover;border-radius:3px;">' : '-'; } },
{ field: 'createTime', title: '发布时间', width: 170, templet: function(d) {
if (!d.createTime) return '-';
var date = new Date(d.createTime * 1000);
return date.getFullYear() + '-' +
String(date.getMonth() + 1).padStart(2, '0') + '-' +
String(date.getDate()).padStart(2, '0') + ' ' +
String(date.getHours()).padStart(2, '0') + ':' +
String(date.getMinutes()).padStart(2, '0') + ':' +
String(date.getSeconds()).padStart(2, '0');
}},
{ width: 160, title: '操作', align: 'center', fixed: 'right', toolbar: '#table-notice-bar' }
]]
});
loadList(1, 20);
table.on('page(LAY-notice-manage)', function (obj) {
loadList(obj.curr, obj.limit);
});
table.on('toolbar(LAY-notice-manage)', function (obj) {
if (obj.event === 'add') {
showNoticeDialog('add', null);
} else if (obj.event === 'refresh') {
loadList(curPage, curLimit);
}
});
table.on('tool(LAY-notice-manage)', function (obj) {
if (obj.event === 'edit') {
$.ajax({
url: baseUrl + '/get',
method: 'get',
headers: { 'token': token },
data: { id: obj.data.id },
success: function (res) {
if (res.code === 0) {
showNoticeDialog('edit', res.data);
} else {
layer.msg(res.info || '获取公告详情失败');
}
}
});
} else if (obj.event === 'delete') {
layer.confirm('确定要删除公告「' + obj.data.title + '」吗?', function (idx) {
$.ajax({
url: baseUrl + '/delete',
method: 'get',
headers: { 'token': token },
data: { id: obj.data.id },
success: function (res) {
layer.close(idx);
if (res.code === 0) {
layer.msg(res.info || '删除成功');
loadList(curPage, curLimit);
} else {
layer.msg(res.info || '删除失败');
}
}
});
});
}
});
function showNoticeDialog(mode, data) {
var isEdit = mode === 'edit';
var title = isEdit ? '编辑公告' : '发布公告';
// 公告图片先上传临时目录,公告保存成功后由服务端移动到 notice 目录。
var uploadUrl = setter.baseurl + 'adminapi/ImageUpload/upload/temp';
var editor = null;
var topImage = isEdit ? (data.topImage || data.TopImage || '') : '';
var contentImages = isEdit ? ((data.contentImages || data.ContentImages || []).slice()) : [];
var html = '<div class="layui-form" lay-filter="notice-form" style="padding:20px 30px 0 0;">' +
'<div class="layui-form-item">' +
'<label class="layui-form-label">公告标题</label>' +
'<div class="layui-input-block">' +
'<input type="text" name="title" lay-verify="required" placeholder="请输入公告标题" class="layui-input" value="' + (isEdit ? escHtml(data.title) : '') + '">' +
'</div>' +
'</div>' +
'<div class="layui-form-item">' +
'<label class="layui-form-label">简介</label>' +
'<div class="layui-input-block">' +
'<input type="text" name="recommend" placeholder="请输入公告简介" class="layui-input" value="' + (isEdit ? escHtml(data.recommend || '') : '') + '">' +
'</div>' +
'</div>' +
'<div class="layui-form-item">' +
'<label class="layui-form-label">顶部图片</label>' +
'<div class="layui-input-block">' +
'<div class="top-image-upload" style="display:flex;align-items:center;gap:10px;">' +
'<button type="button" class="layui-btn layui-btn-sm layui-btn-normal" id="btn-upload-top">选择图片</button>' +
'<div id="top-image-preview" style="max-width:200px;max-height:80px;' + (topImage ? '' : 'display:none') + '">' +
(topImage ? '<img src="' + topImage + '" style="max-width:200px;max-height:80px;border-radius:4px;"><i class="layui-icon layui-icon-close-fill" style="position:relative;left:-16px;top:-30px;cursor:pointer;color:red;font-size:18px;" id="btn-remove-top"></i>' : '') +
'</div>' +
'<input type="hidden" name="topImage" value="' + escHtml(topImage) + '">' +
'</div>' +
'</div>' +
'</div>' +
'<div class="layui-form-item">' +
'<label class="layui-form-label">内容图片</label>' +
'<div class="layui-input-block">' +
'<button type="button" class="layui-btn layui-btn-sm layui-btn-normal" id="btn-upload-content"><i class="layui-icon layui-icon-picture"></i> 添加图片</button>' +
'<div id="content-images-list" style="margin-top:8px;display:flex;flex-wrap:wrap;gap:8px;">' +
(contentImages.length ? contentImages.map(function(img, idx) {
return '<div class="content-img-item" data-index="' + idx + '" style="position:relative;width:120px;height:80px;border:1px solid #e6e6e6;border-radius:4px;overflow:hidden;cursor:move;">' +
'<img src="' + img + '" style="width:100%;height:100%;object-fit:cover;">' +
'<i class="layui-icon layui-icon-close-fill btn-remove-img" style="position:absolute;top:2px;right:2px;cursor:pointer;color:red;font-size:18px;background:#fff;border-radius:50%;" title="删除"></i>' +
'<div style="position:absolute;bottom:0;left:0;right:0;background:rgba(0,0,0,0.5);color:#fff;font-size:11px;text-align:center;">第' + (idx+1) + '张</div>' +
'</div>';
}).join('') : '<div class="empty-tip" style="color:#999;font-size:13px;padding:8px 0;">尚未添加图片</div>') +
'</div>' +
'<input type="hidden" name="contentImages" id="content-images-input" value="' + escHtml(JSON.stringify(contentImages)) + '">' +
'</div>' +
'</div>' +
'<div class="layui-form-item">' +
'<label class="layui-form-label">内容</label>' +
'<div class="layui-input-block">' +
'<div id="notice-content-editor" style="height:360px;border:1px solid #e6e6e6;border-radius:4px;"></div>' +
'</div>' +
'</div>' +
'</div>';
layer.open({
type: 1,
title: title,
area: ['850px', '780px'],
content: html,
btn: ['保存', '取消'],
yes: function (index) {
var titleVal = $('input[name="title"]').val().trim();
var recommendVal = $('input[name="recommend"]').val().trim();
var contentVal = editor ? editor.txt.html().trim() : '';
var topImageVal = $('input[name="topImage"]').val().trim();
var contentImagesVal = [];
try {
contentImagesVal = JSON.parse($('#content-images-input').val() || '[]');
} catch(e) { contentImagesVal = []; }
if (!titleVal) { layer.msg('请输入公告标题'); return; }
if (!contentVal) { layer.msg('请输入公告内容'); return; }
var reqData = isEdit
? { id: data.id, title: titleVal, content: contentVal, recommend: recommendVal, topImage: topImageVal, contentImages: contentImagesVal }
: { title: titleVal, content: contentVal, recommend: recommendVal, topImage: topImageVal, contentImages: contentImagesVal };
ajaxJson(baseUrl + (isEdit ? '/update' : '/add'), reqData, function (res) {
if (res.code === 0) {
layer.close(index);
layer.msg(res.info || (isEdit ? '修改成功' : '发布成功'));
loadList(curPage, curLimit);
} else {
layer.msg(res.info || '操作失败');
}
});
},
success: function (layero) {
form.render();
// 初始化 wangEditor 富文本编辑器
editor = new window.wangEditor('#notice-content-editor');
editor.config.placeholder = '请输入公告内容...';
editor.config.zIndex = 2000;
editor.config.excludeMenus = ['fontName', 'fontSize', 'code', 'codeBlock'];
editor.config.customUploadImg = function (resultFiles, insertImgFn) {
var fd = new FormData();
fd.append('file', resultFiles[0]);
$.ajax({
url: uploadUrl,
method: 'post',
headers: { 'token': token },
data: fd,
processData: false,
contentType: false,
success: function (res) {
if (res.code === 0 && res.data) {
insertImgFn(res.data.path || res.data);
} else {
layer.msg(res.info || '图片上传失败');
}
},
error: function () {
layer.msg('图片上传失败');
}
});
};
editor.create();
if (isEdit && data.content) {
editor.txt.html(data.content);
}
// 顶部图片上传
var fileInput = $('<input type="file" accept="image/*" style="display:none">');
layero.find('#btn-upload-top').after(fileInput);
layero.find('#btn-upload-top').on('click', function() { fileInput.click(); });
fileInput.on('change', function() {
var file = this.files[0];
if (!file) return;
var fd = new FormData();
fd.append('file', file);
$.ajax({
url: uploadUrl,
method: 'post',
headers: { 'token': token },
data: fd,
processData: false,
contentType: false,
success: function(res) {
if (res.code === 0 && res.data) {
var path = res.data.path || res.data;
$('input[name="topImage"]').val(path);
var preview = layero.find('#top-image-preview');
preview.show().html('<img src="' + path + '" style="max-width:200px;max-height:80px;border-radius:4px;"><i class="layui-icon layui-icon-close-fill" style="position:relative;left:-16px;top:-30px;cursor:pointer;color:red;font-size:18px;" id="btn-remove-top"></i>');
bindRemoveTop(layero);
} else {
layer.msg(res.info || '上传失败');
}
}
});
this.value = '';
});
bindRemoveTop(layero);
// 内容图片上传
var contentFileInput = $('<input type="file" accept="image/*" style="display:none">');
layero.find('#btn-upload-content').after(contentFileInput);
layero.find('#btn-upload-content').on('click', function() { contentFileInput.click(); });
contentFileInput.on('change', function() {
var file = this.files[0];
if (!file) return;
var fd = new FormData();
fd.append('file', file);
$.ajax({
url: uploadUrl,
method: 'post',
headers: { 'token': token },
data: fd,
processData: false,
contentType: false,
success: function(res) {
if (res.code === 0 && res.data) {
var path = res.data.path || res.data;
contentImages.push(path);
renderContentImages(layero, contentImages);
} else {
layer.msg(res.info || '上传失败');
}
}
});
this.value = '';
});
// 内容图片拖拽排序
var sortable = function(el) {
var items = el.find('.content-img-item');
var dragIdx = -1;
items.each(function() {
$(this).on('mousedown', function() {
dragIdx = $(this).data('index');
});
});
el.on('mouseup', '.content-img-item', function() {
// 简单的点击交换:拖拽逻辑用 layui 自带的 sort 或简化版
});
};
// 启用拖拽排序(使用原生 drag API 简化实现)
var contentListEl = layero.find('#content-images-list');
contentListEl.on('dragstart', '.content-img-item', function(e) {
e.originalEvent.dataTransfer.setData('text/plain', $(this).data('index'));
});
contentListEl.on('dragover', '.content-img-item', function(e) {
e.preventDefault();
});
contentListEl.on('drop', '.content-img-item', function(e) {
e.preventDefault();
var fromIdx = parseInt(e.originalEvent.dataTransfer.getData('text/plain'));
var toIdx = $(this).data('index');
if (fromIdx !== toIdx && !isNaN(fromIdx) && !isNaN(toIdx)) {
var item = contentImages.splice(fromIdx, 1)[0];
contentImages.splice(toIdx, 0, item);
renderContentImages(layero, contentImages);
}
});
// 内容图片删除
contentListEl.on('click', '.btn-remove-img', function() {
var idx = $(this).closest('.content-img-item').data('index');
contentImages.splice(idx, 1);
renderContentImages(layero, contentImages);
});
}
});
}
function bindRemoveTop(layero) {
layero.find('#btn-remove-top').off().on('click', function() {
$('input[name="topImage"]').val('');
layero.find('#top-image-preview').hide().empty();
});
}
function renderContentImages(layero, images) {
var list = layero.find('#content-images-list');
if (images.length === 0) {
list.html('<div class="empty-tip" style="color:#999;font-size:13px;padding:8px 0;">尚未添加图片</div>');
} else {
list.html(images.map(function(img, idx) {
return '<div class="content-img-item" data-index="' + idx + '" draggable="true" style="position:relative;width:120px;height:80px;border:1px solid #e6e6e6;border-radius:4px;overflow:hidden;cursor:move;display:inline-block;">' +
'<img src="' + img + '" style="width:100%;height:100%;object-fit:cover;">' +
'<i class="layui-icon layui-icon-close-fill btn-remove-img" style="position:absolute;top:2px;right:2px;cursor:pointer;color:red;font-size:18px;background:#fff;border-radius:50%;" title="删除"></i>' +
'<div style="position:absolute;bottom:0;left:0;right:0;background:rgba(0,0,0,0.5);color:#fff;font-size:11px;text-align:center;">第' + (idx+1) + '张</div>' +
'</div>';
}).join(''));
}
$('#content-images-input').val(JSON.stringify(images));
}
function escHtml(str) {
if (!str) return '';
return str.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
});
</script>