添加项目文件。
This commit is contained in:
@@ -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, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user